Data visualization is a critical tool in the data analysis process. Visualization tasks can range from generating fundamental distribution plots to understanding the interplay of complex influential variables in machine learning algorithms. In this chapter we focus on the use of visualization for initial data exploration.

Visual data exploration is a mandatory intial step whether or not more formal analysis follows. When combined with descriptive statistics (Chapter~), visualization provides an effective way to identify summaries, structure, relationships, differences, and abnormalities in the data. Often times no elaborate analysis is necessary as all the important conclusions required for a decision are evident from simple visual examination of the data (REF: Box and Hunter). Other times, data exploration will be used to help guide the data cleaning, feature selection, and sampling process.

Regardless, visual data exploration is about investigating the characteristics of your data set. To do this, we typically create numerous plots in an interactive fashion. This chapter will show you how to create plots that answer some of the fundamental questions we typically have of our data.

library(tidyverse)
library(caret)
(ames <- AmesHousing::make_ames())

Visualizing Continuous Variables

A variable is continuous if it can take any of an infinite set of ordered values. There are several different plots that can effectively communicate the different features of continuous variables. Features we are generally interested in include:

Histograms are often overlooked, yet they are a very efficient means for communicating these features of continuous variables. Formulated by Karl Pearson, histograms display numeric values on the x-axis where the continuous variable is broken into intervals (aka bins) and the the y-axis represents the frequency of observations that fall into that bin. Histograms quickly signal what the most common observations are for the variable being assessed (the higher the bar the more frequent those values are observed in the data); they also signal the shape (spread and symmetr) of your data by illustrating if the observed values cluster towards one end or the other of the distribution.

To get a quick sense of how sales prices are distributed across the 2,930 properties in the ames data we can generate a simple histogram by applying ggplot’s geom_histogram function1. This histogram tells us several important features about our variable:

ggplot(ames, aes(Sale_Price)) +
  geom_histogram()

By default, geom_histogram() will divide your data into 30 equal bins or intervals. Since sales prices range from $12,789 - $755,000, dividing this range into 30 equal bins means the bin width is $24,740. So the first bar will represent the frequency of Sale_Price values that range from about $12,500 to about $37,5002, the second bar represents the income range from about 37,500 to 62,300, and so on.

However, we can control this parameter by changing the bin width argument in geom_histogram. By changing the bin width when doing exploratory analysis you can get a more detailed picture of the relative densities of the distribution. For instance, in the default histogram there was a bin of $136,000 - $161,000 values that had the highest frequency but as the histograms that follow show, we can gather more information as we adjust the binning.

p1 <- ggplot(ames, aes(Sale_Price)) +
  geom_histogram(binwidth = 100000) +
  ggtitle("Bin width = $100,000")
p2 <- ggplot(ames, aes(Sale_Price)) +
  geom_histogram(binwidth = 50000) +
  ggtitle("Bin width = $50,000")
p3 <- ggplot(ames, aes(Sale_Price)) +
  geom_histogram(binwidth = 5000) +
  ggtitle("Bin width = $5,000")
p4 <- ggplot(ames, aes(Sale_Price)) +
  geom_histogram(binwidth = 1000) +
  ggtitle("Bin width = $1,000")
gridExtra::grid.arrange(p1, p2, p3, p4, ncol = 2)

Overall, the histograms consistently show the most common income level to be right around $130,000. We can also find the most frequent bin by combining ggplot2::cut_width (ggplot2::cut_interval and ggplot2::cut_number are additional options) with dplyr::count. We see that the most frequent bin when using increments of $5,000 is $128,000 - $132,000.

ames %>%
  count(cut_width(Sale_Price, width = 5000)) %>%
  arrange(desc(n))

Our histogram with binwidth = 1000 also shows us that there are spikes at specific intervals. This is likely due to home sale prices usually occuring around increments of $5,000. In addition to our primary central tendency (bins with most frequency), we also get a clearer picture of the spread of our variable and its skewness. This suggests there may be a concern with our variable meeting assumptions of normality. If we were to apply an analytic technique that is sensitive to normality assumptions we would likely need to transform our variable.

We can assess the applicability of a log transformation by adding scale_x_log() to our ggplot visual3. This log transformed histogram provides a few new insights:

  1. There is a slight multimodal effect at the top of the distribution suggesting that houses selling in the $150-170K range are not as common as those selling just below and above that price range.
  2. It appears the log transformation helps our variable meet normality assumptions. More on this in a second.
  3. It appears there is a new potential outlier that we did not see earlier. There is at least one observation where the Sale_Price is near zero. In fact, further investigation identifies two observations, one with a Sale_Price of $12,789 and another at $13,100.
ggplot(ames, aes(Sale_Price)) +
  geom_histogram(bins = 100) +
  geom_vline(xintercept = c(150000, 170000), color = "red", lty = "dashed") +
  scale_x_log10(
    labels = scales::dollar, 
    breaks = c(50000, 125000, 300000)
    )

Let’s take a closer look at the second two insights. First, we’ll consider the issue of normality.

If you really want to look at normality, then Q-Q plots are a great visual to assess (Fig xx). This graph plots the cumulative values we have in our data against the cumulative probability of a particular distribution (the default is a normal distribution). In essence, this plot compares the actual value against the expected value that the score should have in a normal distribution. If the data are normally distributed the plot will display a straight (or nearly straight) line. If the data deviates from normality then the line will display strong curvature or “snaking.” These plots illustrate how much the untransformed variable deviates from normality whereas the log transformed values align much closer to a normal distribution.

par(mfrow = c(1, 2))
# non-log transformed
qqnorm(ames$Sale_Price, main = "Untransformed\nNormal Q-Q Plot")
qqline(ames$Sale_Price)
# log transformed
qqnorm(log(ames$Sale_Price), main = "Log Transformed\nNormal Q-Q Plot")
qqline(log(ames$Sale_Price))

I also mentioned how we obtained a new insight regarding a new potential outlier that we did not see earlier. So far our histogram identified potential outliers at the lower end and upper end of the sale price spectrum. Unfortunately histograms are not very good at delineating outliers. Rather, we can use a boxplot which does a better job identifying specific outliers.

Boxplots are an alternative way to illustrate the distribution of a variable and is a concise way to illustrate the standard quantiles and outliers of data. As Figure XX indicates, the box itself extends, left to right, from the 1st quartile to the 3rd quartile. This means that it contains the middle half of the data. The line inside the box is positioned at the median. The lines (whiskers) coming out either side of the box extend to 1.5 interquartile ranges (IQRs) from the quartiles. These generally include most of the data outside the box. More distant values, called outliers, are denoted separately by individual points. Now we have a more analytically specific approach to identifying outliers.

Generic Box Plot

There are two efficient graphs to get an indication of potential outliers in our data. The classic boxplot on the left will identify points beyond the whiskers which are beyond \(1.5*IQR\) from the first and third quantile. This illustrates there are several additional observations that we may need to assess as outliers that were not evident in our histogram. However, when looking at a boxplot we lose insight into the shape of the distribution. A violin plot on the right provides us a similar chart as the boxplot but we lose insight into the quantiles of our data and outliers are not plotted (hence the reason I plot geom_point prior to geom_violin). Violin plots will come in handy later when we start to visualize multiple distributions along side each other.

p1 <- ggplot(ames, aes("var", Sale_Price)) +
  geom_boxplot(outlier.alpha = .25) +
  scale_y_log10(
    labels = scales::dollar, 
    breaks = quantile(ames$Sale_Price)
  )
p2 <- ggplot(ames, aes("var", Sale_Price)) +
  geom_point() +
  geom_violin() +
  scale_y_log10(
    labels = scales::dollar, 
    breaks = quantile(ames$Sale_Price)
  )
gridExtra::grid.arrange(p1, p2, ncol = 2)

The boxplot starts to answer the question of what potential outliers exist in your data. Outliers in data can distort predictions and affect their accuracy. Consequently, its important to understand if outliers are present and, if so, which observations are considered outliers. Boxplots provide a visual assessment of potential outliers while the outliers package provides a number of useful functions to systematically extract these outliers. The most useful function is the scores function, which computes normal, t, chi-squared, IQR and MAD scores of the given data which you can use to find observation(s) that lie beyond a given value.

Here, I use the outliers::score function to extract those observations beyond the whiskers in our boxplot and then use a stem and leaf plot to assess them. A stem and leaf plot is a special table where each data value is split into a “stem” (the first digit or digits) and a “leaf” (usually the second digit). Since the decimal point is located 5 digits to the right of the “|”" the last stem of “7” and and first leaf of “5” means an outlier exists at around $750,000. The last stem of “7” and and second leaf of “6” means an outlier exists at around $760,000. This is a concise way to see approximately where our outliers are. In fact, I can now see that I have 28 lower end outliers ranging from $10,000-$60,000 and 32 upper end outliers ranging from $450,000-$760,000.

outliers <- outliers::scores(log(ames$Sale_Price), type = "iqr", lim = 1.5)
stem(ames$Sale_Price[outliers])

  The decimal point is 5 digit(s) to the right of the |

  0 | 1134444445555555666666666666
  1 | 
  2 | 
  3 | 
  4 | 56666777788899
  5 | 000445566889
  6 | 1123
  7 | 56

Another useful plot for univariate assessment includes the smoothed histogram in which a non-parametric approach is used to estimate the density function. Displaying in density form just means the y-axis is now in a probability scale where the proportion of the given value (or bin of values) to the overall population is displayed. In essence, the y-axis tells you the estimated probability of the x-axis value occurring. This results in a smoothed curve known as the density plot that allows us visualize the distribution. Since the focus of a density plot is to view the overall distribution rather than individual bin observations we lose insight into how many observations occur at certain x values. Consequently, it can be helpful to use geom_rug with geom_density to highlight where clusters, outliers, and gaps of observations are occuring.

p1 <- ggplot(ames, aes(Sale_Price)) +
  geom_density()
p2 <- ggplot(ames, aes(Sale_Price)) +
  geom_density() +
  geom_rug()
gridExtra::grid.arrange(p1, p2, nrow = 1)

Often you will see density plots layered onto histograms. To layer the density plot onto the histogram we need to first draw the histogram but tell ggplot to have the y-axis in density form rather than count. You can then add the geom_density function to add the density plot on top.

ggplot(ames, aes(Sale_Price)) +
  geom_histogram(aes(y = ..density..),
                 binwidth = 5000, color = "grey30", fill = "white") +
  geom_density(alpha = .2, fill = "antiquewhite3")

You may also be interested to see if there are any systematic groupings with how the data is structured. For example, using base R’s plot function with just the Sale_Price will plot the sale price versus the index (row) number of each observation. In the plot below we see a pattern which indicates that groupings of homes with high versus lower sale prices are concentrated together throughout the data set.

plot(ames$Sale_Price)

There are also a couple plots that can come in handy when dealing with smaller data sets. For example, the dotplot below provides more clarity than the histogram for viewing the distribution of mpg in the built-in mtcars dataset with only 32 observations. An alternative to this would be using a strip chart (see stripchart).

p1 <- ggplot(mtcars, aes(x = mpg)) +
  geom_dotplot(method = "histodot", binwidth = 1) +
  ggtitle("dotplot")
p2 <- ggplot(mtcars, aes(x = mpg)) +
  geom_histogram(binwidth = 1) +
  ggtitle("histogram")
gridExtra::grid.arrange(p1, p2, nrow = 1)

As demonstrated, several plots exist for examining univariate continuous variables. Several exampes were provided here but still more exist (i.e. frequency polygon, beanplot, shifted histograms). There is some general advice to follow such as histograms being poor for small data sets, dotplots being poor for large data sets, histograms being poor for identifying outlier cut-offs, boxplots being good for outliers but obscuring multimodality. Conseqently, it is important to draw a variety of plots. Moreover, it is important to adjust parameters within plots (i.e. binwidth, axis transformation for skewed data) to get a comprehensive picture of the variable of concern.

Next, we’ll assess how we can gain insight into distributions of categorical variables.

Visualizing Categorical Variables

A categorical variable is a variable that can take on one of a limited, and usually fixed, number of possible values, assigning each individual or other unit of observation to a particular group or nominal category on the basis of some qualitative property (i.e. gender, grade, manufacturer). There are a few different plots that can effectively communicate features of categorical variables. Features we are generally interested in include:

Bar charts are one of the most commonly used data visualizations for categorical variables. Bar charts display the levels of a categorical variable of interest (typically) along the x-axis and the length of the bar illustrates the value along the y-axis. Consequently, the length of the bar is the primary visual cue in a bar chart and in a univariate visualization this length represents counts of cases in that particular level.

If we look at the general zoning classification for each property sold in our ames dataset we see that the majority of all properties fall within one category. Here, geom_bar simply counts up all observations for each zoning level.

ggplot(ames, aes(MS_Zoning)) +
  geom_bar()

Here, MS_Zoning represents a nominal categorical variable where there is no logical ordering of the labels; they simply represent mutually exclusive levels within our variable. To get better clarity of nominal variables we can make some refinements. Here I use dplyr::count to count the observations in each level prior to plotting. In the second plot I use mutate to compute the percent that each level makes up of all observations. I then feed these summarized data into ggplot where I can reorder the MS_Zoning variable from most frequent to least and then apply coord_flip to rotate the plot and make it easier to read the level categories. Also, notice that now I feeding an x (MS_Zoning) and y (n in the left plot and pct in the right plot) arguments so I apply geom_col rather than geom_bar.

# total count
p1 <- ames %>% 
  count(MS_Zoning) %>%
  ggplot(aes(reorder(MS_Zoning, n), n)) +
  geom_col() +
  coord_flip() +
  ggtitle("Total count")
# percent of whole
p2 <- ames %>% 
  count(MS_Zoning) %>%
  mutate(pct = n / sum(n)) %>%
  ggplot(aes(reorder(MS_Zoning, pct), pct)) +
  geom_col() +
  coord_flip() +
  ggtitle("Percent of whole")
gridExtra::grid.arrange(p1, p2, nrow = 1)

Now we can see that properties zoned as residential low density make up nearly 80% of all observations . We also see that properties zoned as aggricultural (A_agr), industrial (I_all), commercial (C_all), and residential high density make up a very small amount of observations. In fact, below we see that these imbalanced category levels each make up less than 1% of all observations.

ames %>% 
  count(MS_Zoning) %>%
  mutate(pct = n / sum(n)) %>%
  arrange(pct)

This imbalanced nature can cause problems in future analytic models so it may make sense to combine these infrequent levels into an “other” category. An easy way to do that is to use fct_lump.4 Here we use n = 2 to retain the top 2 levels in our variable and condense the remaining into an “other” category. You can see that this combined category still represents less than 10% of all observations.

ames %>% 
  mutate(MS_Zoning = fct_lump(MS_Zoning, n = 2)) %>% 
  count(MS_Zoning) %>%
  mutate(pct = n / sum(n)) %>%
  ggplot(aes(reorder(MS_Zoning, pct), pct)) +
  geom_col() +
  coord_flip()

Basic bar charts such as these are great when the number of category levels is smaller. However, as the number of levels increase the thick nature of the bar can be distracting. Cleveland dot plots and lollipop charts are useful for assessing the frequency or proportion of many levels while minizing the amount of ink on the graphic.

For example, if we assess the frequencies and proportions of home sales by the 38 different neighborhoods a dotplot simplifies the chart.

ames %>%  
  count(Neighborhood) %>%
  mutate(pct = n / sum(n)) %>%
  ggplot(aes(pct, reorder(Neighborhood, pct))) +
  geom_point()

Similar to the Cleveland dot plot, a lollipop chart minimizes the visual ink but uses a line to draw the readers attention to the specific x-axis value achieved by each category. In the lollipop chart we use geom_segment to plot the lines and we explicitly state that we want the lines to start at x = 0 and extend to the neighborhood value with xend = pct. We simply need to include y = neighborhood and yend = neighborhood to tell R the lines are horizontally attached to each neighborhood.

ames %>%  
  count(Neighborhood) %>%
  mutate(pct = n / sum(n)) %>%
  ggplot(aes(pct, reorder(Neighborhood, pct))) +
  geom_point() +
  geom_segment(aes(x = 0, xend = pct, y = Neighborhood, yend = Neighborhood), size = .15)

Sometimes we have categorical data that have natural, ordered categories. These types of categorical variables can be ordinal or interval. An ordinal variable is one in which the order of the values can be important but the differences between each one is not really known. For example, our ames data categorizes the quality of kitchens into five buckets and these buckets have a natural order that is not captured with a regular bar chart.

ggplot(ames, aes(Kitchen_Qual)) + 
  geom_bar()

Here, rather than order by frequency it may be important to order the bars by the natural order of the quality lables: Poor, Fair, Typical, Good, Excellent. This can provide better insight into where most observations fall within this spectrum of quality. To do this we reorder the factor levels with fct_relevel and now its easier to see that most homes have average to slightly above average quality kitchens.

ames %>%
  mutate(Kitchen_Qual = fct_relevel(Kitchen_Qual, "Poor", "Fair", "Typical", "Good")) %>%
  ggplot(aes(Kitchen_Qual)) + 
  geom_bar()

We may also have a categorical variable that has set intervals and may even be identified by integer values. For example, our data identifies the month each home was sold but uses integer values to represent the months. In this case we do not need to reorder our factor levels but we should ensure we visualize these as discrete factor levels (note how I apply factor(Mo_Sold) within ggplot) so that the home sale counts are appropriately bucketed into each month.

p1 <- ggplot(ames, aes(Mo_Sold)) + 
  geom_bar()
p2 <- ggplot(ames, aes(factor(Mo_Sold))) + 
  geom_bar()
gridExtra::grid.arrange(p1, p2, nrow = 2)

Bar charts can also illustrate how our missing values are disbursed across categorical variables. Using the MASS::survey data (since our ames data does not have any missing data) we can make small multiples (more on this in the next section) using facet_wrap to visualize the NAs.

MASS::survey %>%
  select(Sex, Exer, Smoke, Fold, Clap, M.I) %>%
  gather(var, value, Sex:M.I) %>%
  ggplot(aes(value)) +
  geom_bar() +
  facet_wrap(~ var, scales = "free")
attributes are not identical across measure variables;
they will be dropped

Or in some cases observations are not labeled correctly. If we look at the Embarked variable in the titanic package we see that the levels are labeled as C, Q, and S; however, there are two cases that have no label (these values are coded as "" in the actual data set). These are missing values that are just not coded as NAs. For modeling purposes we would likely recode these as either NAs or impute them as one of the other three levels (C, Q, or S).

ggplot(titanic::titanic_train, aes(Embarked)) +
  geom_bar()

Bar charts and their cousins are a simple form of visual display, yet they can provide much information about our categorical variables. Whether viewing nominal, ordinal, or interval data we can make minor adjustments in our bar charts to highlight the important features of our variables.

Visualizing Relationships and Associations for a Continuous Response

Having a solid understanding of univariate distributions is important; however, most analyses want to take the next step understand associations and relationships across different variables. Features we are generally interested in include:

One of the most popular plots to assess association is the scatter plot. The scatter plot helps us to see multiple features between two continuous variables. Here we look the relationship between Sale_Price and total above ground square footage (Gr_Liv_Area). A few features that pop out from this plot includes:

ggplot(ames, aes(x = Gr_Liv_Area, y = Sale_Price)) +
  geom_point(alpha = .3)

This relationship appears to be fairly linear but it is unclear. We can add trend lines to assess the linearity. In the below plot we add a linear line with geom_smooth(method = "lm") and then we add a non-linear line (the second geom_smooth with a specified method adds uses a generalized additive model). This allows us to assess how non-linear a relationship may be. Our new plot shows that for homes with less than 2,250 square feet the relationship is fairly linear; however, beyond 2,250 square feet we see strong deviations from linearity.

Also, note the funneling in the left scatter plot. This is called heteroskedasticity (non-constant variance) and this can cause concerns with certain future modeling approaches (i.e. forms of linear regression). We can assess if transforming our variables can alleviate this concern by adding scale_?_log10. The right plot shows that transforming our variables makes our variability across the plot more constant. We see that for the majority of the plot the relationship is now linear with the exception of the two ends where we see the non-linear line being pulled down. This suggests that there are some influential observations with low and high square footage that are pulling the expected sale price down.

p1 <- ggplot(ames, aes(x = Gr_Liv_Area, y = Sale_Price)) +
  geom_point(alpha = .3) +
  geom_smooth(method = "lm", se = FALSE, color = "red", lty = "dashed") +
  geom_smooth(se = FALSE, lty = "dashed") +
  ggtitle("Non-transformed variables")
p2 <- ggplot(ames, aes(x = Gr_Liv_Area, y = Sale_Price)) +
  geom_point(alpha = .3) +
  geom_smooth(method = "lm", se = FALSE, color = "red", lty = "dashed") +
  geom_smooth(se = FALSE, lty = "dashed") +
  scale_x_log10() +
  scale_y_log10() +
  ggtitle("log-transformed variables")
gridExtra::grid.arrange(p1, p2, nrow = 1)

We can…

p1 <- ggplot(ames, aes(x = Garage_Area, y = Sale_Price)) + 
  geom_point(alpha = .2) +
  scale_y_log10() + 
  scale_x_log10()
p2 <- ggplot(ames, aes(x = Garage_Area, y = Sale_Price)) + 
  geom_point(alpha = .2) + 
  geom_density2d() +
  scale_y_log10() + 
  scale_x_log10()
gridExtra::grid.arrange(p1, p2, nrow = 1)

p1 <- ggplot(ames, aes(x = Bedroom_AbvGr, y = Sale_Price)) +
  geom_point(alpha = .3)
p2 <- ggplot(ames, aes(x = Bedroom_AbvGr, y = Sale_Price)) +
  geom_jitter(alpha = .5, width = .2)
p3 <- ggplot(ames, aes(x = factor(Bedroom_AbvGr), y = Sale_Price)) +
  geom_boxplot()
gridExtra::grid.arrange(p1, p2, p3, nrow = 1)

Visualizing Relationships and Associations for a Categorical Response

Visualizing Data Quality


  1. We could also use hist(ames$Sale_Price) to produce a histogram with base R graphics.

  2. These are approximates because the binning will round to whole numbers (12,800 rather than 12,370.)

  3. Two things to note here. 1. If you want to change the binwidth you either need to feed a log tranformed number to binwidth or, as I did, increase the bins by using bins = 100. 2. If you have values of zero in your variable try `scale_x_continuous(trans = “log1p”), which adds 1 prior to the log transformation.

  4. A great resource to learn more about which you can learn more about managing factors is R for Data Science, Ch. 15.

LS0tCnRpdGxlOiAiQUJBUjogQ2hhcHRlciAzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpEYXRhIHZpc3VhbGl6YXRpb24gaXMgYSBjcml0aWNhbCB0b29sIGluIHRoZSBkYXRhIGFuYWx5c2lzIHByb2Nlc3MuICBWaXN1YWxpemF0aW9uIHRhc2tzIGNhbiByYW5nZSBmcm9tIGdlbmVyYXRpbmcgZnVuZGFtZW50YWwgZGlzdHJpYnV0aW9uIHBsb3RzIHRvIHVuZGVyc3RhbmRpbmcgdGhlIGludGVycGxheSBvZiBjb21wbGV4IGluZmx1ZW50aWFsIHZhcmlhYmxlcyBpbiBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMuICBJbiB0aGlzIGNoYXB0ZXIgd2UgZm9jdXMgb24gdGhlIHVzZSBvZiB2aXN1YWxpemF0aW9uIGZvciBpbml0aWFsICpkYXRhIGV4cGxvcmF0aW9uKi4gCgpWaXN1YWwgZGF0YSBleHBsb3JhdGlvbiBpcyBhIG1hbmRhdG9yeSBpbnRpYWwgc3RlcCB3aGV0aGVyIG9yIG5vdCBtb3JlIGZvcm1hbCBhbmFseXNpcyBmb2xsb3dzLiAgV2hlbiBjb21iaW5lZCB3aXRoIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgKENoYXB0ZXJ+XHJlZntjaDI6ZGVzY3JpcHRpdmV9KSwgdmlzdWFsaXphdGlvbiBwcm92aWRlcyBhbiBlZmZlY3RpdmUgd2F5IHRvIGlkZW50aWZ5IHN1bW1hcmllcywgc3RydWN0dXJlLCByZWxhdGlvbnNoaXBzLCBkaWZmZXJlbmNlcywgYW5kIGFibm9ybWFsaXRpZXMgaW4gdGhlIGRhdGEuICBPZnRlbiB0aW1lcyBubyBlbGFib3JhdGUgYW5hbHlzaXMgaXMgbmVjZXNzYXJ5IGFzIGFsbCB0aGUgaW1wb3J0YW50IGNvbmNsdXNpb25zIHJlcXVpcmVkIGZvciBhIGRlY2lzaW9uIGFyZSBldmlkZW50IGZyb20gc2ltcGxlIHZpc3VhbCBleGFtaW5hdGlvbiBvZiB0aGUgZGF0YSAqKihSRUY6IEJveCBhbmQgSHVudGVyKSoqLiAgT3RoZXIgdGltZXMsIGRhdGEgZXhwbG9yYXRpb24gd2lsbCBiZSB1c2VkIHRvIGhlbHAgZ3VpZGUgdGhlIGRhdGEgY2xlYW5pbmcsIGZlYXR1cmUgc2VsZWN0aW9uLCBhbmQgc2FtcGxpbmcgcHJvY2Vzcy4gIAoKUmVnYXJkbGVzcywgdmlzdWFsIGRhdGEgZXhwbG9yYXRpb24gaXMgYWJvdXQgaW52ZXN0aWdhdGluZyB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHlvdXIgZGF0YSBzZXQuICBUbyBkbyB0aGlzLCB3ZSB0eXBpY2FsbHkgY3JlYXRlIG51bWVyb3VzIHBsb3RzIGluIGFuIGludGVyYWN0aXZlIGZhc2hpb24uICBUaGlzIGNoYXB0ZXIgd2lsbCBzaG93IHlvdSBob3cgdG8gY3JlYXRlIHBsb3RzIHRoYXQgYW5zd2VyIHNvbWUgb2YgdGhlIGZ1bmRhbWVudGFsIHF1ZXN0aW9ucyB3ZSB0eXBpY2FsbHkgaGF2ZSBvZiBvdXIgZGF0YS4gIAoKCmBgYHtyIHBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjYXJldCkKYGBgCgpgYGB7ciBkYXRhLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQooYW1lcyA8LSBBbWVzSG91c2luZzo6bWFrZV9hbWVzKCkpCmBgYAoKYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89RkFMU0V9CnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY29sbGFwc2UgPSBUUlVFLCBmaWcuYWxpZ24gPSAnY2VudGVyJykKYGBgCgojIFZpc3VhbGl6aW5nIENvbnRpbnVvdXMgVmFyaWFibGVzCgpBIHZhcmlhYmxlIGlzIGNvbnRpbnVvdXMgaWYgaXQgY2FuIHRha2UgYW55IG9mIGFuIGluZmluaXRlIHNldCBvZiBvcmRlcmVkIHZhbHVlcy4gVGhlcmUgYXJlIHNldmVyYWwgZGlmZmVyZW50IHBsb3RzIHRoYXQgY2FuIGVmZmVjdGl2ZWx5IGNvbW11bmljYXRlIHRoZSBkaWZmZXJlbnQgZmVhdHVyZXMgb2YgY29udGludW91cyB2YXJpYWJsZXMuICBGZWF0dXJlcyB3ZSBhcmUgZ2VuZXJhbGx5IGludGVyZXN0ZWQgaW4gaW5jbHVkZToKCi0gTWVhc3VyZXMgb2YgbG9jYXRpb24KLSBNZWFzdXJlcyBvZiBzcHJlYWQKLSBBc3ltbWV0cnkKLSBPdXRsaWVycwotIEdhcHMKCgpIaXN0b2dyYW1zIGFyZSBvZnRlbiBvdmVybG9va2VkLCB5ZXQgdGhleSBhcmUgYSB2ZXJ5IGVmZmljaWVudCBtZWFucyBmb3IgY29tbXVuaWNhdGluZyB0aGVzZSBmZWF0dXJlcyBvZiBjb250aW51b3VzIHZhcmlhYmxlcy4gRm9ybXVsYXRlZCBieSBLYXJsIFBlYXJzb24sIGhpc3RvZ3JhbXMgZGlzcGxheSBudW1lcmljIHZhbHVlcyBvbiB0aGUgeC1heGlzIHdoZXJlIHRoZSBjb250aW51b3VzIHZhcmlhYmxlIGlzIGJyb2tlbiBpbnRvIGludGVydmFscyAoYWthIGJpbnMpIGFuZCB0aGUgdGhlIHktYXhpcyByZXByZXNlbnRzIHRoZSBmcmVxdWVuY3kgb2Ygb2JzZXJ2YXRpb25zIHRoYXQgZmFsbCBpbnRvIHRoYXQgYmluLiBIaXN0b2dyYW1zIHF1aWNrbHkgc2lnbmFsIHdoYXQgdGhlIG1vc3QgY29tbW9uIG9ic2VydmF0aW9ucyBhcmUgZm9yIHRoZSB2YXJpYWJsZSBiZWluZyBhc3Nlc3NlZCAodGhlIGhpZ2hlciB0aGUgYmFyIHRoZSBtb3JlIGZyZXF1ZW50IHRob3NlIHZhbHVlcyBhcmUgb2JzZXJ2ZWQgaW4gdGhlIGRhdGEpOyB0aGV5IGFsc28gc2lnbmFsIHRoZSBzaGFwZSAoc3ByZWFkIGFuZCBzeW1tZXRyKSBvZiB5b3VyIGRhdGEgYnkgaWxsdXN0cmF0aW5nIGlmIHRoZSBvYnNlcnZlZCB2YWx1ZXMgY2x1c3RlciB0b3dhcmRzIG9uZSBlbmQgb3IgdGhlIG90aGVyIG9mIHRoZSBkaXN0cmlidXRpb24uCgpUbyBnZXQgYSBxdWljayBzZW5zZSBvZiBob3cgc2FsZXMgcHJpY2VzIGFyZSBkaXN0cmlidXRlZCBhY3Jvc3MgdGhlIDIsOTMwIHByb3BlcnRpZXMgaW4gdGhlIGBhbWVzYCBkYXRhIHdlIGNhbiBnZW5lcmF0ZSBhIHNpbXBsZSBoaXN0b2dyYW0gYnkgYXBwbHlpbmcgZ2dwbG904oCZcyBgZ2VvbV9oaXN0b2dyYW1gIGZ1bmN0aW9uW15iYXNlUmhpc3RdLiBUaGlzIGhpc3RvZ3JhbSB0ZWxscyB1cyBzZXZlcmFsIGltcG9ydGFudCBmZWF0dXJlcyBhYm91dCBvdXIgdmFyaWFibGU6CgotIE1lYXN1cmVzIG9mIGxvY2F0aW9uOiBXZSBjYW4gc2VlIHRoZSBtb3N0IGNvbW1vbiBgU2FsZV9QcmljZWAgaXMgYXJvdW5kIHRoZSBsb3cgJDEwMEsuCi0gTWVhc3VyZXMgb2Ygc3ByZWFkOiBPdXIgYFNhbGVfUHJpY2VgIHJhbmdlcyBmcm9tIG5lYXIgemVybyB0byBvdmVyICQ3MDBLLgotIEFzeW1tZXRyeTogYFNhbGVfUHJpY2VgIGlzIHNrZXdlZCByaWdodCAoYSBjb21tb24gaXNzdWUgd2l0aCBmaW5hbmNpYWwgZGF0YSkuICBEZXBlbmRpbmcgb24gdGhlIGFuYWx5dGljIHRlY2huaXF1ZSB3ZSBtYXkgd2FudCB0byBhcHBseSBsYXRlciBvbiB0aGlzIHN1Z2dlc3RzIHdlIHdpbGwgbGlrZWx5IG5lZWQgdG8gdHJhbnNmb3JtIHRoaXMgdmFyaWFibGUuCi0gT3V0bGllcnM6IEl0IGFwcGVhcnMgdGhhdCB0aGVyZSBhcmUgc29tZSBsYXJnZSB2YWx1ZXMgZmFyIGZyb20gdGhlIG90aGVyIGBTYWxlX1ByaWNlYCB2YWx1ZXMuICBXaGV0aGVyIHRoZXNlIGFyZSBvdXRsaWVycyBpbiB0aGUgbWF0aGVtYXRpY2FsIHNlbnNlIG9yIG91dGxpZXJzIHRvIGJlIGNvbmNlcm5lZCBhYm91dCBpcyBhbm90aGVyIGlzc3VlIGJ1dCBmb3Igbm93IHdlIGF0IGxlYXN0IGtub3cgdGhleSBleGlzdC4KLSBHYXBzOiBXZSBzZWUgYSBnYXAgZXhpc3RzIGJldHdlZW4gYFNhbGVfUHJpY2VgIHZhbHVlcyBhcm91bmQgJDY1MEsgYW5kICQ3MDBLKy4gIAoKYGBge3IgaGlzdDEsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTV9CmdncGxvdChhbWVzLCBhZXMoU2FsZV9QcmljZSkpICsKICBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKQnkgZGVmYXVsdCwgYGdlb21faGlzdG9ncmFtKClgIHdpbGwgZGl2aWRlIHlvdXIgZGF0YSBpbnRvIDMwIGVxdWFsIGJpbnMgb3IgaW50ZXJ2YWxzLiBTaW5jZSBzYWxlcyBwcmljZXMgcmFuZ2UgZnJvbSBcJDEyLDc4OSAtIFwkNzU1LDAwMCwgZGl2aWRpbmcgdGhpcyByYW5nZSBpbnRvIDMwIGVxdWFsIGJpbnMgbWVhbnMgdGhlIGJpbiB3aWR0aCBpcyBcJDI0LDc0MC4gU28gdGhlIGZpcnN0IGJhciB3aWxsIHJlcHJlc2VudCB0aGUgZnJlcXVlbmN5IG9mIGBTYWxlX1ByaWNlYCB2YWx1ZXMgdGhhdCByYW5nZSBmcm9tIGFib3V0IFwkMTIsNTAwIHRvIGFib3V0IFwkMzcsNTAwW15iaW5zXSwgdGhlIHNlY29uZCBiYXIgcmVwcmVzZW50cyB0aGUgaW5jb21lIHJhbmdlIGZyb20gYWJvdXQgMzcsNTAwIHRvIDYyLDMwMCwgYW5kIHNvIG9uLgoKSG93ZXZlciwgd2UgY2FuIGNvbnRyb2wgdGhpcyBwYXJhbWV0ZXIgYnkgY2hhbmdpbmcgdGhlIGJpbiB3aWR0aCBhcmd1bWVudCBpbiBgZ2VvbV9oaXN0b2dyYW1gLiBCeSBjaGFuZ2luZyB0aGUgYmluIHdpZHRoIHdoZW4gZG9pbmcgZXhwbG9yYXRvcnkgYW5hbHlzaXMgeW91IGNhbiBnZXQgYSBtb3JlIGRldGFpbGVkIHBpY3R1cmUgb2YgdGhlIHJlbGF0aXZlIGRlbnNpdGllcyBvZiB0aGUgZGlzdHJpYnV0aW9uLiBGb3IgaW5zdGFuY2UsIGluIHRoZSBkZWZhdWx0IGhpc3RvZ3JhbSB0aGVyZSB3YXMgYSBiaW4gb2YgXCQxMzYsMDAwIC0gXCQxNjEsMDAwIHZhbHVlcyB0aGF0IGhhZCB0aGUgaGlnaGVzdCBmcmVxdWVuY3kgYnV0IGFzIHRoZSBoaXN0b2dyYW1zIHRoYXQgZm9sbG93IHNob3csIHdlIGNhbiBnYXRoZXIgbW9yZSBpbmZvcm1hdGlvbiBhcyB3ZSBhZGp1c3QgdGhlIGJpbm5pbmcuIAoKYGBge3IgaGlzdDIsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTh9CgpwMSA8LSBnZ3Bsb3QoYW1lcywgYWVzKFNhbGVfUHJpY2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMDAwMDApICsKICBnZ3RpdGxlKCJCaW4gd2lkdGggPSAkMTAwLDAwMCIpCgpwMiA8LSBnZ3Bsb3QoYW1lcywgYWVzKFNhbGVfUHJpY2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1MDAwMCkgKwogIGdndGl0bGUoIkJpbiB3aWR0aCA9ICQ1MCwwMDAiKQoKcDMgPC0gZ2dwbG90KGFtZXMsIGFlcyhTYWxlX1ByaWNlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNTAwMCkgKwogIGdndGl0bGUoIkJpbiB3aWR0aCA9ICQ1LDAwMCIpCgpwNCA8LSBnZ3Bsb3QoYW1lcywgYWVzKFNhbGVfUHJpY2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMDAwKSArCiAgZ2d0aXRsZSgiQmluIHdpZHRoID0gJDEsMDAwIikKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHAxLCBwMiwgcDMsIHA0LCBuY29sID0gMikKYGBgCgpPdmVyYWxsLCB0aGUgaGlzdG9ncmFtcyBjb25zaXN0ZW50bHkgc2hvdyB0aGUgbW9zdCBjb21tb24gaW5jb21lIGxldmVsIHRvIGJlIHJpZ2h0IGFyb3VuZCBcJDEzMCwwMDAuIFdlIGNhbiBhbHNvIGZpbmQgdGhlIG1vc3QgZnJlcXVlbnQgYmluIGJ5IGNvbWJpbmluZyBgZ2dwbG90Mjo6Y3V0X3dpZHRoYCAoYGdncGxvdDI6OmN1dF9pbnRlcnZhbGAgYW5kIGBnZ3Bsb3QyOjpjdXRfbnVtYmVyYCBhcmUgYWRkaXRpb25hbCBvcHRpb25zKSB3aXRoIGBkcGx5cjo6Y291bnRgLiBXZSBzZWUgdGhhdCB0aGUgbW9zdCBmcmVxdWVudCBiaW4gd2hlbiB1c2luZyBpbmNyZW1lbnRzIG9mIFwkNSwwMDAgaXMgXCQxMjgsMDAwIC0gXCQxMzIsMDAwLgoKYGBge3J9CmFtZXMgJT4lCiAgY291bnQoY3V0X3dpZHRoKFNhbGVfUHJpY2UsIHdpZHRoID0gNTAwMCkpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkKYGBgCgpPdXIgaGlzdG9ncmFtIHdpdGggYGJpbndpZHRoID0gMTAwMGAgYWxzbyBzaG93cyB1cyB0aGF0IHRoZXJlIGFyZSBzcGlrZXMgYXQgc3BlY2lmaWMgaW50ZXJ2YWxzLiAgVGhpcyBpcyBsaWtlbHkgZHVlIHRvIGhvbWUgc2FsZSBwcmljZXMgdXN1YWxseSBvY2N1cmluZyBhcm91bmQgaW5jcmVtZW50cyBvZiBcJDUsMDAwLiAgSW4gYWRkaXRpb24gdG8gb3VyIHByaW1hcnkgY2VudHJhbCB0ZW5kZW5jeSAoYmlucyB3aXRoIG1vc3QgZnJlcXVlbmN5KSwgd2UgYWxzbyBnZXQgYSBjbGVhcmVyIHBpY3R1cmUgb2YgdGhlIHNwcmVhZCBvZiBvdXIgdmFyaWFibGUgYW5kIGl0cyBza2V3bmVzcy4gIFRoaXMgc3VnZ2VzdHMgdGhlcmUgbWF5IGJlIGEgY29uY2VybiB3aXRoIG91ciB2YXJpYWJsZSBtZWV0aW5nIGFzc3VtcHRpb25zIG9mIG5vcm1hbGl0eS4gIElmIHdlIHdlcmUgdG8gYXBwbHkgYW4gYW5hbHl0aWMgdGVjaG5pcXVlIHRoYXQgaXMgc2Vuc2l0aXZlIHRvIG5vcm1hbGl0eSBhc3N1bXB0aW9ucyB3ZSB3b3VsZCBsaWtlbHkgbmVlZCB0byB0cmFuc2Zvcm0gb3VyIHZhcmlhYmxlLgoKV2UgY2FuIGFzc2VzcyB0aGUgYXBwbGljYWJpbGl0eSBvZiBhIGxvZyB0cmFuc2Zvcm1hdGlvbiBieSBhZGRpbmcgYHNjYWxlX3hfbG9nKClgIHRvIG91ciBnZ3Bsb3QgdmlzdWFsW150cmFuc2ZdLiBUaGlzIGxvZyB0cmFuc2Zvcm1lZCBoaXN0b2dyYW0gcHJvdmlkZXMgYSBmZXcgbmV3IGluc2lnaHRzOgoKMS4gVGhlcmUgaXMgYSBzbGlnaHQgbXVsdGltb2RhbCBlZmZlY3QgYXQgdGhlIHRvcCBvZiB0aGUgZGlzdHJpYnV0aW9uIHN1Z2dlc3RpbmcgdGhhdCBob3VzZXMgc2VsbGluZyBpbiB0aGUgXCQxNTAtMTcwSyByYW5nZSBhcmUgbm90IGFzIGNvbW1vbiBhcyB0aG9zZSBzZWxsaW5nIGp1c3QgYmVsb3cgYW5kIGFib3ZlIHRoYXQgcHJpY2UgcmFuZ2UuCjIuIEl0IGFwcGVhcnMgdGhlIGxvZyB0cmFuc2Zvcm1hdGlvbiBoZWxwcyBvdXIgdmFyaWFibGUgbWVldCBub3JtYWxpdHkgYXNzdW1wdGlvbnMuICBNb3JlIG9uIHRoaXMgaW4gYSBzZWNvbmQuCjMuIEl0IGFwcGVhcnMgdGhlcmUgaXMgYSBuZXcgcG90ZW50aWFsIG91dGxpZXIgdGhhdCB3ZSBkaWQgbm90IHNlZSBlYXJsaWVyLiAgVGhlcmUgaXMgYXQgbGVhc3Qgb25lIG9ic2VydmF0aW9uIHdoZXJlIHRoZSBgU2FsZV9QcmljZWAgaXMgbmVhciB6ZXJvLiAgSW4gZmFjdCwgZnVydGhlciBpbnZlc3RpZ2F0aW9uIGlkZW50aWZpZXMgdHdvIG9ic2VydmF0aW9ucywgb25lIHdpdGggYSBgU2FsZV9QcmljZWAgb2YgXCQxMiw3ODkgYW5kIGFub3RoZXIgYXQgXCQxMywxMDAuCgpgYGB7ciBsb2d0cmFucywgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NX0KZ2dwbG90KGFtZXMsIGFlcyhTYWxlX1ByaWNlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDApICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKDE1MDAwMCwgMTcwMDAwKSwgY29sb3IgPSAicmVkIiwgbHR5ID0gImRhc2hlZCIpICsKICBzY2FsZV94X2xvZzEwKAogICAgbGFiZWxzID0gc2NhbGVzOjpkb2xsYXIsIAogICAgYnJlYWtzID0gYyg1MDAwMCwgMTI1MDAwLCAzMDAwMDApCiAgICApCmBgYAoKTGV0J3MgdGFrZSBhIGNsb3NlciBsb29rIGF0IHRoZSBzZWNvbmQgdHdvIGluc2lnaHRzLiAgRmlyc3QsIHdlJ2xsIGNvbnNpZGVyIHRoZSBpc3N1ZSBvZiBub3JtYWxpdHkuCgpJZiB5b3UgcmVhbGx5IHdhbnQgdG8gbG9vayBhdCBub3JtYWxpdHksIHRoZW4gUS1RIHBsb3RzIGFyZSBhIGdyZWF0IHZpc3VhbCB0byBhc3Nlc3MgKEZpZyB4eCkuIFRoaXMgZ3JhcGggcGxvdHMgdGhlIGN1bXVsYXRpdmUgdmFsdWVzIHdlIGhhdmUgaW4gb3VyIGRhdGEgYWdhaW5zdCB0aGUgY3VtdWxhdGl2ZSBwcm9iYWJpbGl0eSBvZiBhIHBhcnRpY3VsYXIgZGlzdHJpYnV0aW9uICh0aGUgZGVmYXVsdCBpcyBhIG5vcm1hbCBkaXN0cmlidXRpb24pLiBJbiBlc3NlbmNlLCB0aGlzIHBsb3QgY29tcGFyZXMgdGhlIGFjdHVhbCB2YWx1ZSBhZ2FpbnN0IHRoZSBleHBlY3RlZCB2YWx1ZSB0aGF0IHRoZSBzY29yZSBzaG91bGQgaGF2ZSBpbiBhIG5vcm1hbCBkaXN0cmlidXRpb24uIElmIHRoZSBkYXRhIGFyZSBub3JtYWxseSBkaXN0cmlidXRlZCB0aGUgcGxvdCB3aWxsIGRpc3BsYXkgYSBzdHJhaWdodCAob3IgbmVhcmx5IHN0cmFpZ2h0KSBsaW5lLiBJZiB0aGUgZGF0YSBkZXZpYXRlcyBmcm9tIG5vcm1hbGl0eSB0aGVuIHRoZSBsaW5lIHdpbGwgZGlzcGxheSBzdHJvbmcgY3VydmF0dXJlIG9yICJzbmFraW5nLiIgIFRoZXNlIHBsb3RzIGlsbHVzdHJhdGUgaG93IG11Y2ggdGhlIHVudHJhbnNmb3JtZWQgdmFyaWFibGUgZGV2aWF0ZXMgZnJvbSBub3JtYWxpdHkgd2hlcmVhcyB0aGUgbG9nIHRyYW5zZm9ybWVkIHZhbHVlcyBhbGlnbiBtdWNoIGNsb3NlciB0byBhIG5vcm1hbCBkaXN0cmlidXRpb24uCgpgYGB7ciBxcXBsb3QsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTd9CnBhcihtZnJvdyA9IGMoMSwgMikpCgojIG5vbi1sb2cgdHJhbnNmb3JtZWQKcXFub3JtKGFtZXMkU2FsZV9QcmljZSwgbWFpbiA9ICJVbnRyYW5zZm9ybWVkXG5Ob3JtYWwgUS1RIFBsb3QiKQpxcWxpbmUoYW1lcyRTYWxlX1ByaWNlKQoKIyBsb2cgdHJhbnNmb3JtZWQKcXFub3JtKGxvZyhhbWVzJFNhbGVfUHJpY2UpLCBtYWluID0gIkxvZyBUcmFuc2Zvcm1lZFxuTm9ybWFsIFEtUSBQbG90IikKcXFsaW5lKGxvZyhhbWVzJFNhbGVfUHJpY2UpKQpgYGAKCkkgYWxzbyBtZW50aW9uZWQgaG93IHdlIG9idGFpbmVkIGEgbmV3IGluc2lnaHQgcmVnYXJkaW5nIGEgbmV3IHBvdGVudGlhbCBvdXRsaWVyIHRoYXQgd2UgZGlkIG5vdCBzZWUgZWFybGllci4gIFNvIGZhciBvdXIgaGlzdG9ncmFtIGlkZW50aWZpZWQgcG90ZW50aWFsIG91dGxpZXJzIGF0IHRoZSBsb3dlciBlbmQgYW5kIHVwcGVyIGVuZCBvZiB0aGUgc2FsZSBwcmljZSBzcGVjdHJ1bS4gVW5mb3J0dW5hdGVseSBoaXN0b2dyYW1zIGFyZSBub3QgdmVyeSBnb29kIGF0IGRlbGluZWF0aW5nIG91dGxpZXJzLiAgUmF0aGVyLCB3ZSBjYW4gdXNlIGEgYm94cGxvdCB3aGljaCBkb2VzIGEgYmV0dGVyIGpvYiBpZGVudGlmeWluZyBzcGVjaWZpYyBvdXRsaWVycy4KCkJveHBsb3RzIGFyZSBhbiBhbHRlcm5hdGl2ZSB3YXkgdG8gaWxsdXN0cmF0ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgdmFyaWFibGUgYW5kIGlzIGEgY29uY2lzZSB3YXkgdG8gaWxsdXN0cmF0ZSB0aGUgc3RhbmRhcmQgcXVhbnRpbGVzIGFuZCBvdXRsaWVycyBvZiBkYXRhLiBBcyBGaWd1cmUgWFggaW5kaWNhdGVzLCB0aGUgYm94IGl0c2VsZiBleHRlbmRzLCBsZWZ0IHRvIHJpZ2h0LCBmcm9tIHRoZSAxc3QgcXVhcnRpbGUgdG8gdGhlIDNyZCBxdWFydGlsZS4gVGhpcyBtZWFucyB0aGF0IGl0IGNvbnRhaW5zIHRoZSBtaWRkbGUgaGFsZiBvZiB0aGUgZGF0YS4gVGhlIGxpbmUgaW5zaWRlIHRoZSBib3ggaXMgcG9zaXRpb25lZCBhdCB0aGUgbWVkaWFuLiBUaGUgbGluZXMgKHdoaXNrZXJzKSBjb21pbmcgb3V0IGVpdGhlciBzaWRlIG9mIHRoZSBib3ggZXh0ZW5kIHRvIDEuNSBpbnRlcnF1YXJ0aWxlIHJhbmdlcyAoSVFScykgZnJvbSB0aGUgcXVhcnRpbGVzLiBUaGVzZSBnZW5lcmFsbHkgaW5jbHVkZSBtb3N0IG9mIHRoZSBkYXRhIG91dHNpZGUgdGhlIGJveC4gTW9yZSBkaXN0YW50IHZhbHVlcywgY2FsbGVkIG91dGxpZXJzLCBhcmUgZGVub3RlZCBzZXBhcmF0ZWx5IGJ5IGluZGl2aWR1YWwgcG9pbnRzLiBOb3cgd2UgaGF2ZSBhIG1vcmUgYW5hbHl0aWNhbGx5IHNwZWNpZmljIGFwcHJvYWNoIHRvIGlkZW50aWZ5aW5nIG91dGxpZXJzLiAKCjxjZW50ZXI+CjxpbWcgc3JjPSJodHRwczovL3d3dy5sZWFuc2lnbWFjb3Jwb3JhdGlvbi5jb20vd3Avd3AtY29udGVudC91cGxvYWRzLzIwMTUvMTIvQm94LVBsb3QtTVRCXzAxLnBuZyIgYWx0PSJHZW5lcmljIEJveCBQbG90IiB3aWR0aD0iNTAwIiB2c3BhY2U9IjIwIj4KPC9jZW50ZXI+CgpUaGVyZSBhcmUgdHdvIGVmZmljaWVudCBncmFwaHMgdG8gZ2V0IGFuIGluZGljYXRpb24gb2YgcG90ZW50aWFsIG91dGxpZXJzIGluIG91ciBkYXRhLiAgVGhlIGNsYXNzaWMgYm94cGxvdCBvbiB0aGUgbGVmdCB3aWxsIGlkZW50aWZ5IHBvaW50cyBiZXlvbmQgdGhlIHdoaXNrZXJzIHdoaWNoIGFyZSBiZXlvbmQgJDEuNSpJUVIkIGZyb20gdGhlIGZpcnN0IGFuZCB0aGlyZCBxdWFudGlsZS4gIFRoaXMgaWxsdXN0cmF0ZXMgdGhlcmUgYXJlIHNldmVyYWwgYWRkaXRpb25hbCBvYnNlcnZhdGlvbnMgdGhhdCB3ZSBtYXkgbmVlZCB0byBhc3Nlc3MgYXMgb3V0bGllcnMgdGhhdCB3ZXJlIG5vdCBldmlkZW50IGluIG91ciBoaXN0b2dyYW0uICBIb3dldmVyLCB3aGVuIGxvb2tpbmcgYXQgYSBib3hwbG90IHdlIGxvc2UgaW5zaWdodCBpbnRvIHRoZSBzaGFwZSBvZiB0aGUgZGlzdHJpYnV0aW9uLiAgQSB2aW9saW4gcGxvdCBvbiB0aGUgcmlnaHQgcHJvdmlkZXMgdXMgYSBzaW1pbGFyIGNoYXJ0IGFzIHRoZSBib3hwbG90IGJ1dCB3ZSBsb3NlIGluc2lnaHQgaW50byB0aGUgcXVhbnRpbGVzIG9mIG91ciBkYXRhIGFuZCBvdXRsaWVycyBhcmUgbm90IHBsb3R0ZWQgKGhlbmNlIHRoZSByZWFzb24gSSBwbG90IGBnZW9tX3BvaW50YCBwcmlvciB0byBgZ2VvbV92aW9saW5gKS4gIFZpb2xpbiBwbG90cyB3aWxsIGNvbWUgaW4gaGFuZHkgbGF0ZXIgd2hlbiB3ZSBzdGFydCB0byB2aXN1YWxpemUgbXVsdGlwbGUgZGlzdHJpYnV0aW9ucyBhbG9uZyBzaWRlIGVhY2ggb3RoZXIuCgpgYGB7ciBib3hwbG90LCBmaWcuYWxpZ249J2NlbnRlcid9CnAxIDwtIGdncGxvdChhbWVzLCBhZXMoInZhciIsIFNhbGVfUHJpY2UpKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuYWxwaGEgPSAuMjUpICsKICBzY2FsZV95X2xvZzEwKAogICAgbGFiZWxzID0gc2NhbGVzOjpkb2xsYXIsIAogICAgYnJlYWtzID0gcXVhbnRpbGUoYW1lcyRTYWxlX1ByaWNlKQogICkKCnAyIDwtIGdncGxvdChhbWVzLCBhZXMoInZhciIsIFNhbGVfUHJpY2UpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Zpb2xpbigpICsKICBzY2FsZV95X2xvZzEwKAogICAgbGFiZWxzID0gc2NhbGVzOjpkb2xsYXIsIAogICAgYnJlYWtzID0gcXVhbnRpbGUoYW1lcyRTYWxlX1ByaWNlKQogICkKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHAxLCBwMiwgbmNvbCA9IDIpCmBgYAoKVGhlIGJveHBsb3Qgc3RhcnRzIHRvIGFuc3dlciB0aGUgcXVlc3Rpb24gb2Ygd2hhdCBwb3RlbnRpYWwgb3V0bGllcnMgZXhpc3QgaW4geW91ciBkYXRhLiBPdXRsaWVycyBpbiBkYXRhIGNhbiBkaXN0b3J0IHByZWRpY3Rpb25zIGFuZCBhZmZlY3QgdGhlaXIgYWNjdXJhY3kuIENvbnNlcXVlbnRseSwgaXRzIGltcG9ydGFudCB0byB1bmRlcnN0YW5kIGlmIG91dGxpZXJzIGFyZSBwcmVzZW50IGFuZCwgaWYgc28sIHdoaWNoIG9ic2VydmF0aW9ucyBhcmUgY29uc2lkZXJlZCBvdXRsaWVycy4gQm94cGxvdHMgcHJvdmlkZSBhIHZpc3VhbCBhc3Nlc3NtZW50IG9mIHBvdGVudGlhbCBvdXRsaWVycyB3aGlsZSB0aGUgYG91dGxpZXJzYCBwYWNrYWdlIHByb3ZpZGVzIGEgbnVtYmVyIG9mIHVzZWZ1bCBmdW5jdGlvbnMgdG8gc3lzdGVtYXRpY2FsbHkgZXh0cmFjdCB0aGVzZSBvdXRsaWVycy4gVGhlIG1vc3QgdXNlZnVsIGZ1bmN0aW9uIGlzIHRoZSBgc2NvcmVzYCBmdW5jdGlvbiwgd2hpY2ggY29tcHV0ZXMgbm9ybWFsLCB0LCBjaGktc3F1YXJlZCwgSVFSIGFuZCBNQUQgc2NvcmVzIG9mIHRoZSBnaXZlbiBkYXRhIHdoaWNoIHlvdSBjYW4gdXNlIHRvIGZpbmQgb2JzZXJ2YXRpb24ocykgdGhhdCBsaWUgYmV5b25kIGEgZ2l2ZW4gdmFsdWUuCgpIZXJlLCBJIHVzZSB0aGUgYG91dGxpZXJzOjpzY29yZWAgZnVuY3Rpb24gdG8gZXh0cmFjdCB0aG9zZSBvYnNlcnZhdGlvbnMgYmV5b25kIHRoZSB3aGlza2VycyBpbiBvdXIgYm94cGxvdCBhbmQgdGhlbiB1c2UgYSBzdGVtIGFuZCBsZWFmIHBsb3QgdG8gYXNzZXNzIHRoZW0uICBBIHN0ZW0gYW5kIGxlYWYgcGxvdCBpcyBhIHNwZWNpYWwgdGFibGUgd2hlcmUgZWFjaCBkYXRhIHZhbHVlIGlzIHNwbGl0IGludG8gYSAic3RlbSIgKHRoZSBmaXJzdCBkaWdpdCBvciBkaWdpdHMpIGFuZCBhICJsZWFmIiAodXN1YWxseSB0aGUgc2Vjb25kIGRpZ2l0KS4gIFNpbmNlIHRoZSBkZWNpbWFsIHBvaW50IGlzIGxvY2F0ZWQgNSBkaWdpdHMgdG8gdGhlIHJpZ2h0IG9mIHRoZSAifCIiIHRoZSBsYXN0IHN0ZW0gb2YgIjciIGFuZCBhbmQgZmlyc3QgbGVhZiBvZiAiNSIgbWVhbnMgYW4gb3V0bGllciBleGlzdHMgYXQgYXJvdW5kIFwkNzUwLDAwMC4gIFRoZSBsYXN0IHN0ZW0gb2YgIjciIGFuZCBhbmQgc2Vjb25kIGxlYWYgb2YgIjYiIG1lYW5zIGFuIG91dGxpZXIgZXhpc3RzIGF0IGFyb3VuZCBcJDc2MCwwMDAuICBUaGlzIGlzIGEgY29uY2lzZSB3YXkgdG8gc2VlIGFwcHJveGltYXRlbHkgd2hlcmUgb3VyIG91dGxpZXJzIGFyZS4gIEluIGZhY3QsIEkgY2FuIG5vdyBzZWUgdGhhdCBJIGhhdmUgMjggbG93ZXIgZW5kIG91dGxpZXJzIHJhbmdpbmcgZnJvbSBcJDEwLDAwMC1cJDYwLDAwMCBhbmQgMzIgdXBwZXIgZW5kIG91dGxpZXJzIHJhbmdpbmcgZnJvbSBcJDQ1MCwwMDAtXCQ3NjAsMDAwLgoKYGBge3Igc3RlbX0Kb3V0bGllcnMgPC0gb3V0bGllcnM6OnNjb3Jlcyhsb2coYW1lcyRTYWxlX1ByaWNlKSwgdHlwZSA9ICJpcXIiLCBsaW0gPSAxLjUpCnN0ZW0oYW1lcyRTYWxlX1ByaWNlW291dGxpZXJzXSkKYGBgCgpBbm90aGVyIHVzZWZ1bCBwbG90IGZvciB1bml2YXJpYXRlIGFzc2Vzc21lbnQgaW5jbHVkZXMgdGhlICpzbW9vdGhlZCogaGlzdG9ncmFtIGluIHdoaWNoIGEgbm9uLXBhcmFtZXRyaWMgYXBwcm9hY2ggaXMgdXNlZCB0byBlc3RpbWF0ZSB0aGUgZGVuc2l0eSBmdW5jdGlvbi4gRGlzcGxheWluZyBpbiBkZW5zaXR5IGZvcm0ganVzdCBtZWFucyB0aGUgeS1heGlzIGlzIG5vdyBpbiBhIHByb2JhYmlsaXR5IHNjYWxlIHdoZXJlIHRoZSBwcm9wb3J0aW9uIG9mIHRoZSBnaXZlbiB2YWx1ZSAob3IgYmluIG9mIHZhbHVlcykgdG8gdGhlIG92ZXJhbGwgcG9wdWxhdGlvbiBpcyBkaXNwbGF5ZWQuIEluIGVzc2VuY2UsIHRoZSB5LWF4aXMgdGVsbHMgeW91IHRoZSBlc3RpbWF0ZWQgcHJvYmFiaWxpdHkgb2YgdGhlIHgtYXhpcyB2YWx1ZSBvY2N1cnJpbmcuICBUaGlzIHJlc3VsdHMgaW4gYSAqc21vb3RoZWQqIGN1cnZlIGtub3duIGFzIHRoZSBkZW5zaXR5IHBsb3QgdGhhdCBhbGxvd3MgdXMgdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24uICBTaW5jZSB0aGUgZm9jdXMgb2YgYSBkZW5zaXR5IHBsb3QgaXMgdG8gdmlldyB0aGUgb3ZlcmFsbCBkaXN0cmlidXRpb24gcmF0aGVyIHRoYW4gaW5kaXZpZHVhbCBiaW4gb2JzZXJ2YXRpb25zIHdlIGxvc2UgaW5zaWdodCBpbnRvIGhvdyBtYW55IG9ic2VydmF0aW9ucyBvY2N1ciBhdCBjZXJ0YWluIHggdmFsdWVzLiAgQ29uc2VxdWVudGx5LCBpdCBjYW4gYmUgaGVscGZ1bCB0byB1c2UgYGdlb21fcnVnYCB3aXRoIGBnZW9tX2RlbnNpdHlgIHRvIGhpZ2hsaWdodCB3aGVyZSBjbHVzdGVycywgb3V0bGllcnMsIGFuZCBnYXBzIG9mIG9ic2VydmF0aW9ucyBhcmUgb2NjdXJpbmcuCgpgYGB7ciBkZW5zaXR5LCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTR9CnAxIDwtIGdncGxvdChhbWVzLCBhZXMoU2FsZV9QcmljZSkpICsKICBnZW9tX2RlbnNpdHkoKQoKcDIgPC0gZ2dwbG90KGFtZXMsIGFlcyhTYWxlX1ByaWNlKSkgKwogIGdlb21fZGVuc2l0eSgpICsKICBnZW9tX3J1ZygpCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3cgPSAxKQpgYGAKCk9mdGVuIHlvdSB3aWxsIHNlZSBkZW5zaXR5IHBsb3RzIGxheWVyZWQgb250byBoaXN0b2dyYW1zLiBUbyBsYXllciB0aGUgZGVuc2l0eSBwbG90IG9udG8gdGhlIGhpc3RvZ3JhbSB3ZSBuZWVkIHRvIGZpcnN0IGRyYXcgdGhlIGhpc3RvZ3JhbSBidXQgdGVsbCBnZ3Bsb3QgdG8gaGF2ZSB0aGUgeS1heGlzIGluIGRlbnNpdHkgZm9ybSByYXRoZXIgdGhhbiBjb3VudC4gWW91IGNhbiB0aGVuIGFkZCB0aGUgYGdlb21fZGVuc2l0eWAgZnVuY3Rpb24gdG8gYWRkIHRoZSBkZW5zaXR5IHBsb3Qgb24gdG9wLgoKYGBge3IgZGVuc2l0eTIsIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9M30KZ2dwbG90KGFtZXMsIGFlcyhTYWxlX1ByaWNlKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gLi5kZW5zaXR5Li4pLAogICAgICAgICAgICAgICAgIGJpbndpZHRoID0gNTAwMCwgY29sb3IgPSAiZ3JleTMwIiwgZmlsbCA9ICJ3aGl0ZSIpICsKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAuMiwgZmlsbCA9ICJhbnRpcXVld2hpdGUzIikKYGBgCgpZb3UgbWF5IGFsc28gYmUgaW50ZXJlc3RlZCB0byBzZWUgaWYgdGhlcmUgYXJlIGFueSBzeXN0ZW1hdGljIGdyb3VwaW5ncyB3aXRoIGhvdyB0aGUgZGF0YSBpcyBzdHJ1Y3R1cmVkLiAgRm9yIGV4YW1wbGUsIHVzaW5nIGJhc2UgUidzIGBwbG90YCBmdW5jdGlvbiB3aXRoIGp1c3QgdGhlIGBTYWxlX1ByaWNlYCB3aWxsIHBsb3QgdGhlIHNhbGUgcHJpY2UgdmVyc3VzIHRoZSBpbmRleCAocm93KSBudW1iZXIgb2YgZWFjaCBvYnNlcnZhdGlvbi4gIEluIHRoZSBwbG90IGJlbG93IHdlIHNlZSBhIHBhdHRlcm4gd2hpY2ggaW5kaWNhdGVzIHRoYXQgZ3JvdXBpbmdzIG9mIGhvbWVzIHdpdGggaGlnaCB2ZXJzdXMgbG93ZXIgc2FsZSBwcmljZXMgYXJlIGNvbmNlbnRyYXRlZCB0b2dldGhlciB0aHJvdWdob3V0IHRoZSBkYXRhIHNldC4gCgpgYGB7ciBpbmRleHBsb3QsIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9M30KcGxvdChhbWVzJFNhbGVfUHJpY2UpCmBgYAoKCgpUaGVyZSBhcmUgYWxzbyBhIGNvdXBsZSBwbG90cyB0aGF0IGNhbiBjb21lIGluIGhhbmR5IHdoZW4gZGVhbGluZyB3aXRoIHNtYWxsZXIgZGF0YSBzZXRzLiAgRm9yIGV4YW1wbGUsIHRoZSBkb3RwbG90IGJlbG93IHByb3ZpZGVzIG1vcmUgY2xhcml0eSB0aGFuIHRoZSBoaXN0b2dyYW0gZm9yIHZpZXdpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBgbXBnYCBpbiB0aGUgYnVpbHQtaW4gYG10Y2Fyc2AgZGF0YXNldCB3aXRoIG9ubHkgMzIgb2JzZXJ2YXRpb25zLiAgQW4gYWx0ZXJuYXRpdmUgdG8gdGhpcyB3b3VsZCBiZSB1c2luZyBhIHN0cmlwIGNoYXJ0IChzZWUgYHN0cmlwY2hhcnRgKS4gCgpgYGB7ciBkb3RwbG90LCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTR9CnAxIDwtIGdncGxvdChtdGNhcnMsIGFlcyh4ID0gbXBnKSkgKwogIGdlb21fZG90cGxvdChtZXRob2QgPSAiaGlzdG9kb3QiLCBiaW53aWR0aCA9IDEpICsKICBnZ3RpdGxlKCJkb3RwbG90IikKCnAyIDwtIGdncGxvdChtdGNhcnMsIGFlcyh4ID0gbXBnKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKwogIGdndGl0bGUoImhpc3RvZ3JhbSIpCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3cgPSAxKQpgYGAKCkFzIGRlbW9uc3RyYXRlZCwgc2V2ZXJhbCBwbG90cyBleGlzdCBmb3IgZXhhbWluaW5nIHVuaXZhcmlhdGUgY29udGludW91cyB2YXJpYWJsZXMuICBTZXZlcmFsIGV4YW1wZXMgd2VyZSBwcm92aWRlZCBoZXJlIGJ1dCBzdGlsbCBtb3JlIGV4aXN0IChpLmUuIGZyZXF1ZW5jeSBwb2x5Z29uLCBiZWFucGxvdCwgc2hpZnRlZCBoaXN0b2dyYW1zKS4gIFRoZXJlIGlzIHNvbWUgZ2VuZXJhbCBhZHZpY2UgdG8gZm9sbG93IHN1Y2ggYXMgaGlzdG9ncmFtcyBiZWluZyBwb29yIGZvciBzbWFsbCBkYXRhIHNldHMsIGRvdHBsb3RzIGJlaW5nIHBvb3IgZm9yIGxhcmdlIGRhdGEgc2V0cywgaGlzdG9ncmFtcyBiZWluZyBwb29yIGZvciBpZGVudGlmeWluZyBvdXRsaWVyIGN1dC1vZmZzLCBib3hwbG90cyBiZWluZyBnb29kIGZvciBvdXRsaWVycyBidXQgb2JzY3VyaW5nIG11bHRpbW9kYWxpdHkuICBDb25zZXFlbnRseSwgaXQgaXMgaW1wb3J0YW50IHRvIGRyYXcgYSB2YXJpZXR5IG9mIHBsb3RzLiAgTW9yZW92ZXIsIGl0IGlzIGltcG9ydGFudCB0byBhZGp1c3QgcGFyYW1ldGVycyB3aXRoaW4gcGxvdHMgKGkuZS4gYmlud2lkdGgsIGF4aXMgdHJhbnNmb3JtYXRpb24gZm9yIHNrZXdlZCBkYXRhKSB0byBnZXQgYSBjb21wcmVoZW5zaXZlIHBpY3R1cmUgb2YgdGhlIHZhcmlhYmxlIG9mIGNvbmNlcm4uCgpOZXh0LCB3ZSdsbCBhc3Nlc3MgaG93IHdlIGNhbiBnYWluIGluc2lnaHQgaW50byBkaXN0cmlidXRpb25zIG9mIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4KCiMgVmlzdWFsaXppbmcgQ2F0ZWdvcmljYWwgVmFyaWFibGVzCgpBIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGlzIGEgdmFyaWFibGUgdGhhdCBjYW4gdGFrZSBvbiBvbmUgb2YgYSBsaW1pdGVkLCBhbmQgdXN1YWxseSBmaXhlZCwgbnVtYmVyIG9mIHBvc3NpYmxlIHZhbHVlcywgYXNzaWduaW5nIGVhY2ggaW5kaXZpZHVhbCBvciBvdGhlciB1bml0IG9mIG9ic2VydmF0aW9uIHRvIGEgcGFydGljdWxhciBncm91cCBvciBub21pbmFsIGNhdGVnb3J5IG9uIHRoZSBiYXNpcyBvZiBzb21lIHF1YWxpdGF0aXZlIHByb3BlcnR5IChpLmUuIGdlbmRlciwgZ3JhZGUsIG1hbnVmYWN0dXJlcikuIFRoZXJlIGFyZSBhIGZldyBkaWZmZXJlbnQgcGxvdHMgdGhhdCBjYW4gZWZmZWN0aXZlbHkgY29tbXVuaWNhdGUgZmVhdHVyZXMgb2YgY2F0ZWdvcmljYWwgdmFyaWFibGVzLiAgRmVhdHVyZXMgd2UgYXJlIGdlbmVyYWxseSBpbnRlcmVzdGVkIGluIGluY2x1ZGU6CgotIENvdW50IG9mIGVhY2ggY2F0ZWdvcnkKLSBQcm9wb3J0aW9uIG9mIGVhY2ggY2F0ZWdvcnkKLSBJbWJhbGFuY2VkIGNhdGVnb3JpZXMKLSBNaXNsYWJlbGVkIGNhdGVnb3JpZXMKCkJhciBjaGFydHMgYXJlIG9uZSBvZiB0aGUgbW9zdCBjb21tb25seSB1c2VkIGRhdGEgdmlzdWFsaXphdGlvbnMgZm9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gQmFyIGNoYXJ0cyBkaXNwbGF5IHRoZSBsZXZlbHMgb2YgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBvZiBpbnRlcmVzdCAodHlwaWNhbGx5KSBhbG9uZyB0aGUgeC1heGlzIGFuZCB0aGUgbGVuZ3RoIG9mIHRoZSBiYXIgaWxsdXN0cmF0ZXMgdGhlIHZhbHVlIGFsb25nIHRoZSB5LWF4aXMuIENvbnNlcXVlbnRseSwgdGhlIGxlbmd0aCBvZiB0aGUgYmFyIGlzIHRoZSBwcmltYXJ5IHZpc3VhbCBjdWUgaW4gYSBiYXIgY2hhcnQgYW5kIGluIGEgdW5pdmFyaWF0ZSB2aXN1YWxpemF0aW9uIHRoaXMgbGVuZ3RoIHJlcHJlc2VudHMgY291bnRzIG9mIGNhc2VzIGluIHRoYXQgcGFydGljdWxhciBsZXZlbC4KCklmIHdlIGxvb2sgYXQgdGhlIGdlbmVyYWwgem9uaW5nIGNsYXNzaWZpY2F0aW9uIGZvciBlYWNoIHByb3BlcnR5IHNvbGQgaW4gb3VyIGBhbWVzYCBkYXRhc2V0IHdlIHNlZSB0aGF0IHRoZSBtYWpvcml0eSBvZiBhbGwgcHJvcGVydGllcyBmYWxsIHdpdGhpbiBvbmUgY2F0ZWdvcnkuIEhlcmUsIGBnZW9tX2JhcmAgc2ltcGx5IGNvdW50cyB1cCBhbGwgb2JzZXJ2YXRpb25zIGZvciBlYWNoIHpvbmluZyBsZXZlbC4KCmBgYHtyIGJhcjEsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTR9CmdncGxvdChhbWVzLCBhZXMoTVNfWm9uaW5nKSkgKwogIGdlb21fYmFyKCkKYGBgCgpIZXJlLCBgTVNfWm9uaW5nYCByZXByZXNlbnRzIGEgbm9taW5hbCBjYXRlZ29yaWNhbCB2YXJpYWJsZSB3aGVyZSB0aGVyZSBpcyBubyBsb2dpY2FsIG9yZGVyaW5nIG9mIHRoZSBsYWJlbHM7IHRoZXkgc2ltcGx5IHJlcHJlc2VudCBtdXR1YWxseSBleGNsdXNpdmUgbGV2ZWxzIHdpdGhpbiBvdXIgdmFyaWFibGUuICBUbyBnZXQgYmV0dGVyIGNsYXJpdHkgb2Ygbm9taW5hbCB2YXJpYWJsZXMgd2UgY2FuIG1ha2Ugc29tZSByZWZpbmVtZW50cy4gIEhlcmUgSSB1c2UgYGRwbHlyOjpjb3VudGAgdG8gY291bnQgdGhlIG9ic2VydmF0aW9ucyBpbiBlYWNoIGxldmVsIHByaW9yIHRvIHBsb3R0aW5nLiAgSW4gdGhlIHNlY29uZCBwbG90IEkgdXNlIGBtdXRhdGVgIHRvIGNvbXB1dGUgdGhlIHBlcmNlbnQgdGhhdCBlYWNoIGxldmVsIG1ha2VzIHVwIG9mIGFsbCBvYnNlcnZhdGlvbnMuICBJIHRoZW4gZmVlZCB0aGVzZSBzdW1tYXJpemVkIGRhdGEgaW50byBgZ2dwbG90YCB3aGVyZSBJIGNhbiBgcmVvcmRlcmAgdGhlIGBNU19ab25pbmdgIHZhcmlhYmxlIGZyb20gbW9zdCBmcmVxdWVudCB0byBsZWFzdCBhbmQgdGhlbiBhcHBseSBgY29vcmRfZmxpcGAgdG8gcm90YXRlIHRoZSBwbG90IGFuZCBtYWtlIGl0IGVhc2llciB0byByZWFkIHRoZSBsZXZlbCBjYXRlZ29yaWVzLiAgQWxzbywgbm90aWNlIHRoYXQgbm93IEkgZmVlZGluZyBhbiB4IChgTVNfWm9uaW5nYCkgYW5kIHkgKGBuYCBpbiB0aGUgbGVmdCBwbG90IGFuZCBgcGN0YCBpbiB0aGUgcmlnaHQgcGxvdCkgYXJndW1lbnRzIHNvIEkgYXBwbHkgYGdlb21fY29sYCByYXRoZXIgdGhhbiBgZ2VvbV9iYXJgLgoKYGBge3IgYmFyMiwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9M30KIyB0b3RhbCBjb3VudApwMSA8LSBhbWVzICU+JSAKICBjb3VudChNU19ab25pbmcpICU+JQogIGdncGxvdChhZXMocmVvcmRlcihNU19ab25pbmcsIG4pLCBuKSkgKwogIGdlb21fY29sKCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgZ2d0aXRsZSgiVG90YWwgY291bnQiKQoKIyBwZXJjZW50IG9mIHdob2xlCnAyIDwtIGFtZXMgJT4lIAogIGNvdW50KE1TX1pvbmluZykgJT4lCiAgbXV0YXRlKHBjdCA9IG4gLyBzdW0obikpICU+JQogIGdncGxvdChhZXMocmVvcmRlcihNU19ab25pbmcsIHBjdCksIHBjdCkpICsKICBnZW9tX2NvbCgpICsKICBjb29yZF9mbGlwKCkgKwogIGdndGl0bGUoIlBlcmNlbnQgb2Ygd2hvbGUiKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDEsIHAyLCBucm93ID0gMSkKYGBgCgpOb3cgd2UgY2FuIHNlZSB0aGF0IHByb3BlcnRpZXMgem9uZWQgYXMgcmVzaWRlbnRpYWwgbG93IGRlbnNpdHkgbWFrZSB1cCBuZWFybHkgODAlIG9mIGFsbCBvYnNlcnZhdGlvbnMgLiBXZSBhbHNvIHNlZSB0aGF0IHByb3BlcnRpZXMgem9uZWQgYXMgYWdncmljdWx0dXJhbCAoYEFfYWdyYCksIGluZHVzdHJpYWwgKGBJX2FsbGApLCBjb21tZXJjaWFsIChgQ19hbGxgKSwgYW5kIHJlc2lkZW50aWFsIGhpZ2ggZGVuc2l0eSBtYWtlIHVwIGEgdmVyeSBzbWFsbCBhbW91bnQgb2Ygb2JzZXJ2YXRpb25zLiAgSW4gZmFjdCwgYmVsb3cgd2Ugc2VlIHRoYXQgdGhlc2UgaW1iYWxhbmNlZCBjYXRlZ29yeSBsZXZlbHMgZWFjaCBtYWtlIHVwIGxlc3MgdGhhbiAxXCUgb2YgYWxsIG9ic2VydmF0aW9ucy4gIAoKYGBge3J9CmFtZXMgJT4lIAogIGNvdW50KE1TX1pvbmluZykgJT4lCiAgbXV0YXRlKHBjdCA9IG4gLyBzdW0obikpICU+JQogIGFycmFuZ2UocGN0KQpgYGAKClRoaXMgaW1iYWxhbmNlZCBuYXR1cmUgY2FuIGNhdXNlIHByb2JsZW1zIGluIGZ1dHVyZSBhbmFseXRpYyBtb2RlbHMgc28gaXQgbWF5IG1ha2Ugc2Vuc2UgdG8gY29tYmluZSB0aGVzZSBpbmZyZXF1ZW50IGxldmVscyBpbnRvIGFuICJvdGhlciIgY2F0ZWdvcnkuIEFuIGVhc3kgd2F5IHRvIGRvIHRoYXQgaXMgdG8gdXNlIGBmY3RfbHVtcGAuW15mYWN0b3JzXSAgSGVyZSB3ZSB1c2UgYG4gPSAyYCB0byByZXRhaW4gdGhlIHRvcCAyIGxldmVscyBpbiBvdXIgdmFyaWFibGUgYW5kIGNvbmRlbnNlIHRoZSByZW1haW5pbmcgaW50byBhbiAib3RoZXIiIGNhdGVnb3J5LiAgWW91IGNhbiBzZWUgdGhhdCB0aGlzIGNvbWJpbmVkIGNhdGVnb3J5IHN0aWxsIHJlcHJlc2VudHMgbGVzcyB0aGFuIDEwJSBvZiBhbGwgb2JzZXJ2YXRpb25zLgoKYGBge3IgYmFyMywgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9M30KYW1lcyAlPiUgCiAgbXV0YXRlKE1TX1pvbmluZyA9IGZjdF9sdW1wKE1TX1pvbmluZywgbiA9IDIpKSAlPiUgCiAgY291bnQoTVNfWm9uaW5nKSAlPiUKICBtdXRhdGUocGN0ID0gbiAvIHN1bShuKSkgJT4lCiAgZ2dwbG90KGFlcyhyZW9yZGVyKE1TX1pvbmluZywgcGN0KSwgcGN0KSkgKwogIGdlb21fY29sKCkgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCkJhc2ljIGJhciBjaGFydHMgc3VjaCBhcyB0aGVzZSBhcmUgZ3JlYXQgd2hlbiB0aGUgbnVtYmVyIG9mIGNhdGVnb3J5IGxldmVscyBpcyBzbWFsbGVyLiAgSG93ZXZlciwgYXMgdGhlIG51bWJlciBvZiBsZXZlbHMgaW5jcmVhc2UgdGhlIHRoaWNrIG5hdHVyZSBvZiB0aGUgYmFyIGNhbiBiZSBkaXN0cmFjdGluZy4gIENsZXZlbGFuZCBkb3QgcGxvdHMgYW5kIGxvbGxpcG9wIGNoYXJ0cyBhcmUgdXNlZnVsIGZvciBhc3Nlc3NpbmcgdGhlIGZyZXF1ZW5jeSBvciBwcm9wb3J0aW9uIG9mIG1hbnkgbGV2ZWxzIHdoaWxlIG1pbml6aW5nIHRoZSBhbW91bnQgb2YgaW5rIG9uIHRoZSBncmFwaGljLgoKRm9yIGV4YW1wbGUsIGlmIHdlIGFzc2VzcyB0aGUgZnJlcXVlbmNpZXMgYW5kIHByb3BvcnRpb25zIG9mIGhvbWUgc2FsZXMgYnkgIHRoZSAzOCBkaWZmZXJlbnQgbmVpZ2hib3Job29kcyBhIGRvdHBsb3Qgc2ltcGxpZmllcyB0aGUgY2hhcnQuCgpgYGB7ciBkb3QsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTd9CmFtZXMgJT4lICAKICBjb3VudChOZWlnaGJvcmhvb2QpICU+JQogIG11dGF0ZShwY3QgPSBuIC8gc3VtKG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHBjdCwgcmVvcmRlcihOZWlnaGJvcmhvb2QsIHBjdCkpKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKU2ltaWxhciB0byB0aGUgQ2xldmVsYW5kIGRvdCBwbG90LCBhIGxvbGxpcG9wIGNoYXJ0IG1pbmltaXplcyB0aGUgdmlzdWFsIGluayBidXQgdXNlcyBhIGxpbmUgdG8gZHJhdyB0aGUgcmVhZGVycyBhdHRlbnRpb24gdG8gdGhlIHNwZWNpZmljIHgtYXhpcyB2YWx1ZSBhY2hpZXZlZCBieSBlYWNoIGNhdGVnb3J5LiBJbiB0aGUgbG9sbGlwb3AgY2hhcnQgd2UgdXNlIGBnZW9tX3NlZ21lbnRgIHRvIHBsb3QgdGhlIGxpbmVzIGFuZCB3ZSBleHBsaWNpdGx5IHN0YXRlIHRoYXQgd2Ugd2FudCB0aGUgbGluZXMgdG8gc3RhcnQgYXQgYHggPSAwYCBhbmQgZXh0ZW5kIHRvIHRoZSBuZWlnaGJvcmhvb2QgdmFsdWUgd2l0aCBgeGVuZCA9IHBjdGAuIFdlIHNpbXBseSBuZWVkIHRvIGluY2x1ZGUgYHkgPSBuZWlnaGJvcmhvb2RgIGFuZCBgeWVuZCA9IG5laWdoYm9yaG9vZGAgdG8gdGVsbCBSIHRoZSBsaW5lcyBhcmUgaG9yaXpvbnRhbGx5IGF0dGFjaGVkIHRvIGVhY2ggbmVpZ2hib3Job29kLgoKYGBge3IgZG90MSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9N30KYW1lcyAlPiUgIAogIGNvdW50KE5laWdoYm9yaG9vZCkgJT4lCiAgbXV0YXRlKHBjdCA9IG4gLyBzdW0obikpICU+JQogIGdncGxvdChhZXMocGN0LCByZW9yZGVyKE5laWdoYm9yaG9vZCwgcGN0KSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSBwY3QsIHkgPSBOZWlnaGJvcmhvb2QsIHllbmQgPSBOZWlnaGJvcmhvb2QpLCBzaXplID0gLjE1KQpgYGAKCgpTb21ldGltZXMgd2UgaGF2ZSBjYXRlZ29yaWNhbCBkYXRhIHRoYXQgaGF2ZSBuYXR1cmFsLCBvcmRlcmVkIGNhdGVnb3JpZXMuICBUaGVzZSB0eXBlcyBvZiBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgY2FuIGJlIG9yZGluYWwgb3IgaW50ZXJ2YWwuICBBbiBvcmRpbmFsIHZhcmlhYmxlIGlzIG9uZSBpbiB3aGljaCB0aGUgb3JkZXIgb2YgdGhlIHZhbHVlcyBjYW4gYmUgaW1wb3J0YW50IGJ1dCB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBlYWNoIG9uZSBpcyBub3QgcmVhbGx5IGtub3duLiAgRm9yIGV4YW1wbGUsIG91ciBgYW1lc2AgZGF0YSBjYXRlZ29yaXplcyB0aGUgcXVhbGl0eSBvZiBraXRjaGVucyBpbnRvIGZpdmUgYnVja2V0cyBhbmQgdGhlc2UgYnVja2V0cyBoYXZlIGEgbmF0dXJhbCBvcmRlciB0aGF0IGlzIG5vdCBjYXB0dXJlZCB3aXRoIGEgcmVndWxhciBiYXIgY2hhcnQuCgpgYGB7ciBvcmQxLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD01fQpnZ3Bsb3QoYW1lcywgYWVzKEtpdGNoZW5fUXVhbCkpICsgCiAgZ2VvbV9iYXIoKQpgYGAKCkhlcmUsIHJhdGhlciB0aGFuIG9yZGVyIGJ5IGZyZXF1ZW5jeSBpdCBtYXkgYmUgaW1wb3J0YW50IHRvIG9yZGVyIHRoZSBiYXJzIGJ5IHRoZSBuYXR1cmFsIG9yZGVyIG9mIHRoZSBxdWFsaXR5IGxhYmxlczogIFBvb3IsIEZhaXIsIFR5cGljYWwsIEdvb2QsIEV4Y2VsbGVudC4gIFRoaXMgY2FuIHByb3ZpZGUgYmV0dGVyIGluc2lnaHQgaW50byB3aGVyZSBtb3N0IG9ic2VydmF0aW9ucyBmYWxsIHdpdGhpbiB0aGlzIHNwZWN0cnVtIG9mIHF1YWxpdHkuICBUbyBkbyB0aGlzIHdlIHJlb3JkZXIgdGhlIGZhY3RvciBsZXZlbHMgd2l0aCBgZmN0X3JlbGV2ZWxgIGFuZCBub3cgaXRzIGVhc2llciB0byBzZWUgdGhhdCBtb3N0IGhvbWVzIGhhdmUgYXZlcmFnZSB0byBzbGlnaHRseSBhYm92ZSBhdmVyYWdlIHF1YWxpdHkga2l0Y2hlbnMuCgpgYGB7ciBvcmQyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD01fQphbWVzICU+JQogIG11dGF0ZShLaXRjaGVuX1F1YWwgPSBmY3RfcmVsZXZlbChLaXRjaGVuX1F1YWwsICJQb29yIiwgIkZhaXIiLCAiVHlwaWNhbCIsICJHb29kIikpICU+JQogIGdncGxvdChhZXMoS2l0Y2hlbl9RdWFsKSkgKyAKICBnZW9tX2JhcigpCmBgYAoKV2UgbWF5IGFsc28gaGF2ZSBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHRoYXQgaGFzIHNldCBpbnRlcnZhbHMgYW5kIG1heSBldmVuIGJlIGlkZW50aWZpZWQgYnkgaW50ZWdlciB2YWx1ZXMuICBGb3IgZXhhbXBsZSwgb3VyIGRhdGEgaWRlbnRpZmllcyB0aGUgbW9udGggZWFjaCBob21lIHdhcyBzb2xkIGJ1dCB1c2VzIGludGVnZXIgdmFsdWVzIHRvIHJlcHJlc2VudCB0aGUgbW9udGhzLiAgSW4gdGhpcyBjYXNlIHdlIGRvIG5vdCBuZWVkIHRvIHJlb3JkZXIgb3VyIGZhY3RvciBsZXZlbHMgYnV0IHdlIHNob3VsZCBlbnN1cmUgd2UgdmlzdWFsaXplIHRoZXNlIGFzIGRpc2NyZXRlIGZhY3RvciBsZXZlbHMgKG5vdGUgaG93IEkgYXBwbHkgYGZhY3RvcihNb19Tb2xkKWAgd2l0aGluIGdncGxvdCkgc28gdGhhdCB0aGUgaG9tZSBzYWxlIGNvdW50cyBhcmUgYXBwcm9wcmlhdGVseSBidWNrZXRlZCBpbnRvIGVhY2ggbW9udGguICAKCmBgYHtyIGludDEsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTV9CnAxIDwtIGdncGxvdChhbWVzLCBhZXMoTW9fU29sZCkpICsgCiAgZ2VvbV9iYXIoKQoKcDIgPC0gZ2dwbG90KGFtZXMsIGFlcyhmYWN0b3IoTW9fU29sZCkpKSArIAogIGdlb21fYmFyKCkKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdyA9IDIpCmBgYAoKCkJhciBjaGFydHMgY2FuIGFsc28gaWxsdXN0cmF0ZSBob3cgb3VyIG1pc3NpbmcgdmFsdWVzIGFyZSBkaXNidXJzZWQgYWNyb3NzIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gIFVzaW5nIHRoZSBgTUFTUzo6c3VydmV5YCBkYXRhIChzaW5jZSBvdXIgYGFtZXNgIGRhdGEgZG9lcyBub3QgaGF2ZSBhbnkgbWlzc2luZyBkYXRhKSB3ZSBjYW4gbWFrZSBzbWFsbCBtdWx0aXBsZXMgKG1vcmUgb24gdGhpcyBpbiB0aGUgbmV4dCBzZWN0aW9uKSB1c2luZyBgZmFjZXRfd3JhcGAgdG8gdmlzdWFsaXplIHRoZSBgTkFgcy4gCgpgYGB7ciBtaXNzaW5nMSwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9OH0KTUFTUzo6c3VydmV5ICU+JQogIHNlbGVjdChTZXgsIEV4ZXIsIFNtb2tlLCBGb2xkLCBDbGFwLCBNLkkpICU+JQogIGdhdGhlcih2YXIsIHZhbHVlLCBTZXg6TS5JKSAlPiUKICBnZ3Bsb3QoYWVzKHZhbHVlKSkgKwogIGdlb21fYmFyKCkgKwogIGZhY2V0X3dyYXAofiB2YXIsIHNjYWxlcyA9ICJmcmVlIikKYGBgCgpPciBpbiBzb21lIGNhc2VzIG9ic2VydmF0aW9ucyBhcmUgbm90IGxhYmVsZWQgY29ycmVjdGx5LiAgSWYgd2UgbG9vayBhdCB0aGUgYEVtYmFya2VkYCB2YXJpYWJsZSBpbiB0aGUgYHRpdGFuaWNgIHBhY2thZ2Ugd2Ugc2VlIHRoYXQgdGhlIGxldmVscyBhcmUgbGFiZWxlZCBhcyBDLCBRLCBhbmQgUzsgaG93ZXZlciwgdGhlcmUgYXJlIHR3byBjYXNlcyB0aGF0IGhhdmUgbm8gbGFiZWwgKHRoZXNlIHZhbHVlcyBhcmUgY29kZWQgYXMgYCIiYCBpbiB0aGUgYWN0dWFsIGRhdGEgc2V0KS4gIFRoZXNlIGFyZSBtaXNzaW5nIHZhbHVlcyB0aGF0IGFyZSBqdXN0IG5vdCBjb2RlZCBhcyBgTkFgcy4gIEZvciBtb2RlbGluZyBwdXJwb3NlcyB3ZSB3b3VsZCBsaWtlbHkgcmVjb2RlIHRoZXNlIGFzIGVpdGhlciBgTkFgcyBvciBpbXB1dGUgdGhlbSBhcyBvbmUgb2YgdGhlIG90aGVyIHRocmVlIGxldmVscyAoQywgUSwgb3IgUykuCgpgYGB7ciBtaXNsYWJlbGVkLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00fQpnZ3Bsb3QodGl0YW5pYzo6dGl0YW5pY190cmFpbiwgYWVzKEVtYmFya2VkKSkgKwogIGdlb21fYmFyKCkKYGBgCgpCYXIgY2hhcnRzIGFuZCB0aGVpciBjb3VzaW5zIGFyZSBhIHNpbXBsZSBmb3JtIG9mIHZpc3VhbCBkaXNwbGF5LCB5ZXQgdGhleSBjYW4gcHJvdmlkZSBtdWNoIGluZm9ybWF0aW9uIGFib3V0IG91ciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuICBXaGV0aGVyIHZpZXdpbmcgbm9taW5hbCwgb3JkaW5hbCwgb3IgaW50ZXJ2YWwgZGF0YSB3ZSBjYW4gbWFrZSBtaW5vciBhZGp1c3RtZW50cyBpbiBvdXIgYmFyIGNoYXJ0cyB0byBoaWdobGlnaHQgdGhlIGltcG9ydGFudCBmZWF0dXJlcyBvZiBvdXIgdmFyaWFibGVzLgoKIyBWaXN1YWxpemluZyBSZWxhdGlvbnNoaXBzIGFuZCBBc3NvY2lhdGlvbnMgZm9yIGEgQ29udGludW91cyBSZXNwb25zZQoKSGF2aW5nIGEgc29saWQgdW5kZXJzdGFuZGluZyBvZiB1bml2YXJpYXRlIGRpc3RyaWJ1dGlvbnMgaXMgaW1wb3J0YW50OyBob3dldmVyLCBtb3N0IGFuYWx5c2VzIHdhbnQgdG8gdGFrZSB0aGUgbmV4dCBzdGVwIHVuZGVyc3RhbmQgYXNzb2NpYXRpb25zIGFuZCByZWxhdGlvbnNoaXBzIGFjcm9zcyBkaWZmZXJlbnQgdmFyaWFibGVzLiBGZWF0dXJlcyB3ZSBhcmUgZ2VuZXJhbGx5IGludGVyZXN0ZWQgaW4gaW5jbHVkZToKCi0gQXNzb2NpYXRpb25zCi0gT3V0bGllcnMKLSBDbHVzdGVycwotIEdhcHMKLSBCYXJyaWVycwotIENoYW5nZSBwb2ludHMKCgpPbmUgb2YgdGhlIG1vc3QgcG9wdWxhciBwbG90cyB0byBhc3Nlc3MgYXNzb2NpYXRpb24gaXMgdGhlIHNjYXR0ZXIgcGxvdC4gIFRoZSBzY2F0dGVyIHBsb3QgaGVscHMgdXMgdG8gc2VlIG11bHRpcGxlIGZlYXR1cmVzIGJldHdlZW4gdHdvIGNvbnRpbnVvdXMgdmFyaWFibGVzLiAgSGVyZSB3ZSBsb29rIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgU2FsZV9QcmljZWAgYW5kIHRvdGFsIGFib3ZlIGdyb3VuZCBzcXVhcmUgZm9vdGFnZSAoYEdyX0xpdl9BcmVhYCkuIEEgZmV3IGZlYXR1cmVzIHRoYXQgcG9wIG91dCBmcm9tIHRoaXMgcGxvdCBpbmNsdWRlczoKCi0gQXNzb2NpYXRpb25zOiBUaGVyZSBpcyBhIHBvc2l0aXZlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZXNlIHR3byB2YXJpYWJsZXMuICBBcyB0b3RhbCBhYm92ZSBncm91bmQgc3F1YXJlIGZvb3RhZ2UgaW5jcmVhc2VzIHRoZSBzYWxlIHByaWNlIGFsc28gaW5jcmVhc2VzLgotIE91dGxpZXJzOiBTZXZlcmFsIG91dGxpZXJzIGFwcGVhciBpbiBtdWx0aXBsZSBkaXJlY3Rpb25zLiAgVHdvIG91dGxpZXJzIGFwcGVhciBhdCB0aGUgdG9wIG9mIHRoZSBjaGFydCBzdWdnZXN0aW5nIHRoZXNlIGFyZSBsYXJnZXIgdGhhbiBub3JtYWwgaG9tZXMgdGhhdCBzb2xkIGZvciB2ZXJ5IGhpZ2ggcHJpY2VzLiAgV2UgYWxzbyBzZWUgdGhyZWUgb3V0bGllcnMgYXQgdGhlIGZhciByaWdodCBvZiB0aGUgY2hhcnQgc3VnZ2VzdGluZyB0aGVzZSBob21lcyBoYXZlIHZlcnkgbGFyZ2Ugc3F1YXJlIGZvb3RhZ2UgYnV0IHNvbGQgZm9yIGF2ZXJhZ2Ugc2FsZSBwcmljZXMuCi0gQ2x1c3RlcnM6IEdpdmUgdGhlIGxhcmdlIG51bWJlciBvZiBwb2ludHMgdGhlcmUgaXMgYSBsb3Qgb2Ygb3ZlcnBsb3R0aW5nLCB3aGljaCBpcyB3aHkgSSBpbmNvcnBvcmF0ZWQgYGFscGhhID0gLjNgIHRvIGluY3JlYXNlIHRyYW5zcGFyZW5jeS4gIFRoaXMgYWxsb3dzIHVzIHRvIHNlZSB0aGUgY2x1c3RlcmluZyBvZiBkYXRhIHBvaW50cyBpbiB0aGUgY2VudGVyIG9mIHRoZSB2YXJpYWJsZSByZWxhdGlvbnNoaXAuCi0gQmFycmllcnM6IFRoZSBvdXRlciBsaW1pdHMgb2Ygb3VyIHBvaW50IGNsdXN0ZXJpbmcgc2hvd3MgdXMgdGhhdCB0aGVyZSBhcmUgbGltaXRhdGlvbnMgb24gdGhlIHNhbGUgcHJpY2UgZm9yIGdpdmVuIHJhbmdlcyBvZiBzcXVhcmUgZm9vdGFnZS4gIEZvciBleGFtcGxlLCBob21lcyB3aXRoIGxlc3MgdGhhbiAxLDAwMCBzcXVhcmUgZmVldCBhYm92ZSBncm91bmQgYXBwZWFyIHRvIGhhdmUgYSBwcmljZSBjZWlsaW5nIG9mIFwkMjAwLDAwMCBvciBsZXNzLgoKYGBge3J9CmdncGxvdChhbWVzLCBhZXMoeCA9IEdyX0xpdl9BcmVhLCB5ID0gU2FsZV9QcmljZSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gLjMpCmBgYAoKVGhpcyByZWxhdGlvbnNoaXAgYXBwZWFycyB0byBiZSBmYWlybHkgbGluZWFyIGJ1dCBpdCBpcyB1bmNsZWFyLiBXZSBjYW4gYWRkIHRyZW5kIGxpbmVzIHRvIGFzc2VzcyB0aGUgbGluZWFyaXR5LiAgSW4gdGhlIGJlbG93IHBsb3Qgd2UgYWRkIGEgbGluZWFyIGxpbmUgd2l0aCBgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIilgIGFuZCB0aGVuIHdlIGFkZCBhIG5vbi1saW5lYXIgbGluZSAodGhlIHNlY29uZCBgZ2VvbV9zbW9vdGhgIHdpdGggYSBzcGVjaWZpZWQgYG1ldGhvZGAgYWRkcyB1c2VzIGEgZ2VuZXJhbGl6ZWQgYWRkaXRpdmUgbW9kZWwpLiAgVGhpcyBhbGxvd3MgdXMgdG8gYXNzZXNzIGhvdyBub24tbGluZWFyIGEgcmVsYXRpb25zaGlwIG1heSBiZS4gIE91ciBuZXcgcGxvdCBzaG93cyB0aGF0IGZvciBob21lcyB3aXRoIGxlc3MgdGhhbiAyLDI1MCBzcXVhcmUgZmVldCB0aGUgcmVsYXRpb25zaGlwIGlzIGZhaXJseSBsaW5lYXI7IGhvd2V2ZXIsIGJleW9uZCAyLDI1MCBzcXVhcmUgZmVldCB3ZSBzZWUgc3Ryb25nIGRldmlhdGlvbnMgZnJvbSBsaW5lYXJpdHkuCgpBbHNvLCBub3RlIHRoZSBmdW5uZWxpbmcgaW4gdGhlIGxlZnQgc2NhdHRlciBwbG90LiAgVGhpcyBpcyBjYWxsZWQgaGV0ZXJvc2tlZGFzdGljaXR5IChub24tY29uc3RhbnQgdmFyaWFuY2UpIGFuZCB0aGlzIGNhbiBjYXVzZSBjb25jZXJucyB3aXRoIGNlcnRhaW4gZnV0dXJlIG1vZGVsaW5nIGFwcHJvYWNoZXMgKGkuZS4gZm9ybXMgb2YgbGluZWFyIHJlZ3Jlc3Npb24pLiAgV2UgY2FuIGFzc2VzcyBpZiB0cmFuc2Zvcm1pbmcgb3VyIHZhcmlhYmxlcyBjYW4gYWxsZXZpYXRlIHRoaXMgY29uY2VybiBieSBhZGRpbmcgYHNjYWxlXz9fbG9nMTBgLiAgVGhlIHJpZ2h0IHBsb3Qgc2hvd3MgdGhhdCB0cmFuc2Zvcm1pbmcgb3VyIHZhcmlhYmxlcyBtYWtlcyBvdXIgdmFyaWFiaWxpdHkgYWNyb3NzIHRoZSBwbG90IG1vcmUgY29uc3RhbnQuICBXZSBzZWUgdGhhdCBmb3IgdGhlIG1ham9yaXR5IG9mIHRoZSBwbG90IHRoZSByZWxhdGlvbnNoaXAgaXMgbm93IGxpbmVhciB3aXRoIHRoZSBleGNlcHRpb24gb2YgdGhlIHR3byBlbmRzIHdoZXJlIHdlIHNlZSB0aGUgbm9uLWxpbmVhciBsaW5lIGJlaW5nIHB1bGxlZCBkb3duLiAgVGhpcyBzdWdnZXN0cyB0aGF0IHRoZXJlIGFyZSBzb21lIGluZmx1ZW50aWFsIG9ic2VydmF0aW9ucyB3aXRoIGxvdyBhbmQgaGlnaCBzcXVhcmUgZm9vdGFnZSB0aGF0IGFyZSBwdWxsaW5nIHRoZSBleHBlY3RlZCBzYWxlIHByaWNlIGRvd24uICAKCgpgYGB7ciBkYmxfc2NhdHRlciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NH0KcDEgPC0gZ2dwbG90KGFtZXMsIGFlcyh4ID0gR3JfTGl2X0FyZWEsIHkgPSBTYWxlX1ByaWNlKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAuMykgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG9yID0gInJlZCIsIGx0eSA9ICJkYXNoZWQiKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSwgbHR5ID0gImRhc2hlZCIpICsKICBnZ3RpdGxlKCJOb24tdHJhbnNmb3JtZWQgdmFyaWFibGVzIikKCnAyIDwtIGdncGxvdChhbWVzLCBhZXMoeCA9IEdyX0xpdl9BcmVhLCB5ID0gU2FsZV9QcmljZSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gLjMpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJyZWQiLCBsdHkgPSAiZGFzaGVkIikgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UsIGx0eSA9ICJkYXNoZWQiKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIGdndGl0bGUoImxvZy10cmFuc2Zvcm1lZCB2YXJpYWJsZXMiKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDEsIHAyLCBucm93ID0gMSkKYGBgCgpXZSBjYW4uLi4KCmBgYHtyIGRlbnNpdHlfcGxvdCwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9M30KcDEgPC0gZ2dwbG90KGFtZXMsIGFlcyh4ID0gR2FyYWdlX0FyZWEsIHkgPSBTYWxlX1ByaWNlKSkgKyAKICBnZW9tX3BvaW50KGFscGhhID0gLjIpICsKICBzY2FsZV95X2xvZzEwKCkgKyAKICBzY2FsZV94X2xvZzEwKCkKCnAyIDwtIGdncGxvdChhbWVzLCBhZXMoeCA9IEdhcmFnZV9BcmVhLCB5ID0gU2FsZV9QcmljZSkpICsgCiAgZ2VvbV9wb2ludChhbHBoYSA9IC4yKSArIAogIGdlb21fZGVuc2l0eTJkKCkgKwogIHNjYWxlX3lfbG9nMTAoKSArIAogIHNjYWxlX3hfbG9nMTAoKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDEsIHAyLCBucm93ID0gMSkKYGBgCgoKCmBgYHtyIGludF9zY2F0dGVyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0zfQpwMSA8LSBnZ3Bsb3QoYW1lcywgYWVzKHggPSBCZWRyb29tX0FidkdyLCB5ID0gU2FsZV9QcmljZSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gLjMpCgpwMiA8LSBnZ3Bsb3QoYW1lcywgYWVzKHggPSBCZWRyb29tX0FidkdyLCB5ID0gU2FsZV9QcmljZSkpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IC41LCB3aWR0aCA9IC4yKQoKcDMgPC0gZ2dwbG90KGFtZXMsIGFlcyh4ID0gZmFjdG9yKEJlZHJvb21fQWJ2R3IpLCB5ID0gU2FsZV9QcmljZSkpICsKICBnZW9tX2JveHBsb3QoKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDEsIHAyLCBwMywgbnJvdyA9IDEpCmBgYAoKCgoKLSBzY2F0dGVyIHBsb3RzIFwmIGRlbnNpdHkgcGxvdHMKLSBGYWNldHRlZCBoaXN0b2dyYW1zIFwmIGpveSBwbG90cwotIHBhcmFsbGVsIGNvb3JkIHBsb3RzCgoKIyBWaXN1YWxpemluZyBSZWxhdGlvbnNoaXBzIGFuZCBBc3NvY2lhdGlvbnMgZm9yIGEgQ2F0ZWdvcmljYWwgUmVzcG9uc2UKCi0gZmFjZXR0ZWQgYmFyIGNoYXJ0cwotIG1vc2FpYyBwbG90Ci0gaGVhdG1hcAoKIyBWaXN1YWxpemluZyBEYXRhIFF1YWxpdHkKCi0gTWlzc2luZ25lc3MKLSBPdXRsaWVycwoKClteYmFzZVJoaXN0XTogV2UgY291bGQgYWxzbyB1c2UgYGhpc3QoYW1lcyRTYWxlX1ByaWNlKWAgdG8gcHJvZHVjZSBhIGhpc3RvZ3JhbSB3aXRoIGJhc2UgUiBncmFwaGljcy4KW15iaW5zXTogVGhlc2UgYXJlIGFwcHJveGltYXRlcyBiZWNhdXNlIHRoZSBiaW5uaW5nIHdpbGwgcm91bmQgdG8gd2hvbGUgbnVtYmVycyAoMTIsODAwIHJhdGhlciB0aGFuIDEyLDM3MC4pCltedHJhbnNmXTogVHdvIHRoaW5ncyB0byBub3RlIGhlcmUuIDEuIElmIHlvdSB3YW50IHRvIGNoYW5nZSB0aGUgYmlud2lkdGggeW91IGVpdGhlciBuZWVkIHRvIGZlZWQgYSBsb2cgdHJhbmZvcm1lZCBudW1iZXIgdG8gYmlud2lkdGggb3IsIGFzIEkgZGlkLCBpbmNyZWFzZSB0aGUgYmlucyBieSB1c2luZyBgYmlucyA9IDEwMGAuIDIuIElmIHlvdSBoYXZlIHZhbHVlcyBvZiB6ZXJvIGluIHlvdXIgdmFyaWFibGUgdHJ5IGBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMXAiKSwgd2hpY2ggYWRkcyAxIHByaW9yIHRvIHRoZSBsb2cgdHJhbnNmb3JtYXRpb24uClteZmFjdG9yc106IEEgZ3JlYXQgcmVzb3VyY2UgdG8gbGVhcm4gbW9yZSBhYm91dCB3aGljaCB5b3UgY2FuIGxlYXJuIG1vcmUgYWJvdXQgbWFuYWdpbmcgZmFjdG9ycyBpcyBSIGZvciBEYXRhIFNjaWVuY2UsIENoLiAxNS4K